X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=6410d570853afec660e81ff245edbe423ec354ee;hb=4cb209b121430b38606932e31d2659bd7ed57447;hp=b623f05aa4ac6c5c205880ec24d8daa09f3c6515;hpb=d77c0707e0f06a3d6ce2ce1e3b69feb3a890df3e;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index b623f05aa4..6410d57085 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -314,13 +314,15 @@ public: /// Output the surrogate pair formed by \p c and \p next to \p os. /// \return the number of characters written. - int latexSurrogatePair(otexstream & os, char_type c, char_type next, + int latexSurrogatePair(BufferParams const &, otexstream & os, + char_type c, char_type next, OutputParams const &); /// Output a space in appropriate formatting (or a surrogate pair /// if the next character is a combining character). /// \return whether a surrogate pair was output. - bool simpleTeXBlanks(OutputParams const &, + bool simpleTeXBlanks(BufferParams const &, + OutputParams const &, otexstream &, pos_type i, unsigned int & column, @@ -330,7 +332,8 @@ public: /// Output consecutive unicode chars, belonging to the same script as /// specified by the latex macro \p ltx, to \p os starting from \p i. /// \return the number of characters written. - int writeScriptChars(OutputParams const &, otexstream & os, + int writeScriptChars(BufferParams const &, OutputParams const &, + otexstream & os, docstring const & ltx, Change const &, Encoding const &, std::string const, pos_type & i); @@ -867,8 +870,9 @@ int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges) } -int Paragraph::Private::latexSurrogatePair(otexstream & os, char_type c, - char_type next, OutputParams const & runparams) +int Paragraph::Private::latexSurrogatePair(BufferParams const & bparams, + otexstream & os, char_type c, char_type next, + OutputParams const & runparams) { // Writing next here may circumvent a possible font change between // c and next. Since next is only output if it forms a surrogate pair @@ -887,41 +891,61 @@ int Paragraph::Private::latexSurrogatePair(otexstream & os, char_type c, } docstring latex2 = encoding.latexChar(c).first; + if (docstring(1, next) == latex1) { + // The encoding supports the combination: + // output as is (combining char after base char). + os << latex2 << latex1; + return latex1.length() + latex2.length(); + } + + // Handle combining characters in "script" context (i.e., \textgreek and \textcyrillic) docstring::size_type const brace1 = latex2.find_first_of(from_ascii("{")); docstring::size_type const brace2 = latex2.find_last_of(from_ascii("}")); string script = to_ascii(latex2.substr(1, brace1 - 1)); + // "Script chars" need to embraced in \textcyrillic and \textgreek notwithstanding + // whether they are encodable or not (it only depends on the font encoding) + if (!runparams.isFullUnicode()) + // This will get us a script value to deal with below + Encodings::isKnownScriptChar(c, script); int pos = 0; int length = brace2; string fontenc; if (runparams.local_font) - fontenc = runparams.local_font->language()->fontenc(); + fontenc = runparams.local_font->language()->fontenc(bparams); else - fontenc = runparams.main_fontenc; - if ((script == "textgreek" && fontenc == "LGR") - ||(script == "textcyr" && (fontenc == "T2A" || fontenc == "T2B" - || fontenc == "T2C" || fontenc == "X2"))) { - // Correct font encoding is being used, so we can avoid \text[greek|cyr]. + fontenc = bparams.language->fontenc(bparams); + docstring scriptmacro; + docstring cb; + if (script == "textgreek" || script == "textcyrillic") { + // We separate the script macro (\text[greek|cyr]) from the rest, + // since we need to include the combining char in it (#6463). + // This is "the rest": pos = brace1 + 1; length -= pos; latex2 = latex2.substr(pos, length); + // We only need the script macro with non-native font encodings + if (Encodings::needsScriptWrapper(script, fontenc)) { + scriptmacro = from_ascii("\\" + script + "{"); + cb = from_ascii("}"); + } } - if (docstring(1, next) == latex1) { - // the encoding supports the combination - os << latex2 << latex1; - return latex1.length() + latex2.length(); - } else if (runparams.local_font && - runparams.local_font->language()->lang() == "polutonikogreek") { - // polutonikogreek only works without the brackets - os << latex1 << latex2; - return latex1.length() + latex2.length(); - } else - os << latex1 << '{' << latex2 << '}'; - return latex1.length() + latex2.length() + 2; + docstring lb; + docstring rb; + // polutonikogreek does not play nice with brackets + if (!runparams.local_font + || runparams.local_font->language()->lang() != "polutonikogreek") { + lb = from_ascii("{"); + rb = from_ascii("}"); + } + + os << scriptmacro << latex1 << lb << latex2 << rb << cb; + return latex1.length() + latex2.length() + lb.length() + rb.length() + cb.length(); } -bool Paragraph::Private::simpleTeXBlanks(OutputParams const & runparams, +bool Paragraph::Private::simpleTeXBlanks(BufferParams const & bparams, + OutputParams const & runparams, otexstream & os, pos_type i, unsigned int & column, @@ -935,7 +959,7 @@ bool Paragraph::Private::simpleTeXBlanks(OutputParams const & runparams, char_type next = text_[i + 1]; if (Encodings::isCombiningChar(next)) { // This space has an accent, so we must always output it. - column += latexSurrogatePair(os, ' ', next, runparams) - 1; + column += latexSurrogatePair(bparams, os, ' ', next, runparams) - 1; return true; } } @@ -966,7 +990,8 @@ bool Paragraph::Private::simpleTeXBlanks(OutputParams const & runparams, } -int Paragraph::Private::writeScriptChars(OutputParams const & runparams, +int Paragraph::Private::writeScriptChars(BufferParams const & bparams, + OutputParams const & runparams, otexstream & os, docstring const & ltx, Change const & runningChange, @@ -992,9 +1017,7 @@ int Paragraph::Private::writeScriptChars(OutputParams const & runparams, int pos = 0; int length = brace2; bool closing_brace = true; - if ((script == "textgreek" && fontenc == "LGR") - ||(script == "textcyr" && (fontenc == "T2A" || fontenc == "T2B" - || fontenc == "T2C" || fontenc == "X2"))) { + if (!Encodings::needsScriptWrapper(script, fontenc)) { // Correct font encoding is being used, so we can avoid \text[greek|cyr]. pos = brace1 + 1; length -= pos; @@ -1030,7 +1053,7 @@ int Paragraph::Private::writeScriptChars(OutputParams const & runparams, if (i + 2 < size) { next_next = text_[i + 2]; if (Encodings::isCombiningChar(next_next)) { - length += latexSurrogatePair(os, next, next_next, runparams) - 1; + length += latexSurrogatePair(bparams, os, next, next_next, runparams) - 1; i += 2; continue; } @@ -1211,10 +1234,7 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, pos_type end_pos, unsigned int & column) { - // With polyglossia, brackets and stuff need not be reversed - // in RTL scripts (see bug #8251) - char_type const c = (runparams.use_polyglossia) ? - owner_->getUChar(bparams, i) : text_[i]; + char_type const c = owner_->getUChar(bparams, runparams, i); if (style.pass_thru || runparams.pass_thru || contains(style.pass_thru_chars, c) @@ -1345,7 +1365,7 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, if (i + 1 < int(text_.size())) { next = text_[i + 1]; if (Encodings::isCombiningChar(next)) { - column += latexSurrogatePair(os, c, next, runparams) - 1; + column += latexSurrogatePair(bparams, os, c, next, runparams) - 1; ++i; break; } @@ -1372,16 +1392,17 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, } } string fontenc; - if (running_font.language()->lang() == runparams.document_language) - fontenc = runparams.main_fontenc; - else - fontenc = running_font.language()->fontenc(); - if (Encodings::isKnownScriptChar(c, script) - && prefixIs(latex.first, from_ascii("\\" + script))) - column += writeScriptChars(runparams, os, latex.first, - running_change, encoding, - fontenc, i) - 1; - else if (latex.second + fontenc = running_font.language()->fontenc(bparams); + // "Script chars" need to embraced in \textcyrillic and \textgreek notwithstanding + // whether they are encodable or not (it only depends on the font encoding) + if (!runparams.isFullUnicode() && Encodings::isKnownScriptChar(c, script)) { + docstring const wrapper = from_ascii("\\" + script + "{"); + docstring ltx = latex.first; + if (!prefixIs(ltx, wrapper)) + ltx = wrapper + latex.first + from_ascii("}"); + column += writeScriptChars(bparams, runparams, os, ltx, running_change, + encoding, fontenc, i) - 1; + } else if (latex.second && ((!prefixIs(nextlatex, '\\') && !prefixIs(nextlatex, '{') && !prefixIs(nextlatex, '}')) @@ -1528,17 +1549,54 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const ? features.buffer().masterParams() : features.buffer().params(); for (pos_type i = 0; i < int(text_.size()) ; ++i) { char_type c = text_[i]; + CharInfo const & ci = Encodings::unicodeCharInfo(c); if (c == 0x0022) { if (features.runparams().isFullUnicode() && bp.useNonTeXFonts) features.require("textquotedblp"); else if (bp.main_font_encoding() != "T1" || ((&owner_->getFontSettings(bp, i))->language()->internalFontEncoding())) features.require("textquotedbl"); - } - if (!bp.use_dash_ligatures - && (c == 0x2013 || c == 0x2014) - && bp.useNonTeXFonts - && features.runparams().flavor == OutputParams::XETEX) + } else if (ci.textfeature() && contains(ci.textpreamble(), '=')) { + // features that depend on the font or input encoding + string feats = ci.textpreamble(); + string fontenc = (&owner_->getFontSettings(bp, i))->language()->fontenc(bp); + if (fontenc.empty()) + fontenc = features.runparams().main_fontenc; + while (!feats.empty()) { + string feat; + feats = split(feats, feat, ','); + if (contains(feat, "!=")) { + // a feature that is required except for the spcified + // font or input encodings + string realfeature; + string const contexts = ltrim(split(feat, realfeature, '!'), "="); + // multiple encodings are separated by semicolon + vector context = getVectorFromString(contexts, ";"); + // require feature if the context matches neither current font + // nor input encoding + if (std::find(context.begin(), context.end(), fontenc) == context.end() + && std::find(context.begin(), context.end(), + features.runparams().encoding->name()) == context.end()) + features.require(realfeature); + } else if (contains(feat, '=')) { + // a feature that is required only for the spcified + // font or input encodings + string realfeature; + string const contexts = split(feat, realfeature, '='); + // multiple encodings are separated by semicolon + vector context = getVectorFromString(contexts, ";"); + // require feature if the context matches either current font + // or input encoding + if (std::find(context.begin(), context.end(), fontenc) != context.end() + || std::find(context.begin(), context.end(), + features.runparams().encoding->name()) != context.end()) + features.require(realfeature); + } + } + } else if (!bp.use_dash_ligatures + && (c == 0x2013 || c == 0x2014) + && bp.useNonTeXFonts + && features.runparams().flavor == OutputParams::XETEX) // XeTeX's dash behaviour is determined via a global setting features.require("xetexdashbreakstate"); BufferEncodings::validate(c, features); @@ -1730,7 +1788,9 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, void Paragraph::validate(LaTeXFeatures & features) const { d->validate(features); - if (needsCProtection()) + bool fragile = features.runparams().moving_arg; + fragile |= layout().needprotect; + if (needsCProtection(fragile)) features.require("cprotect"); } @@ -1934,32 +1994,60 @@ Font const Paragraph::getLayoutFont } -char_type Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const +char_type Paragraph::getUChar(BufferParams const & bparams, + OutputParams const & rp, + pos_type pos) const { char_type c = d->text_[pos]; + + // Return unchanged character in LTR languages. if (!getFontSettings(bparams, pos).isRightToLeft()) return c; - // FIXME: The arabic special casing is due to the difference of arabic - // round brackets input introduced in r18599. Check if this should be - // unified with Hebrew or at least if all bracket types should be - // handled the same (file format change in either case). + // FIXME This is a complete mess due to all the language-specific + // special cases. We need to unify this eventually, but this + // requires a file format change and some thought. + // We also need to unify the input of parentheses in different RTL + // languages. Currently, some have their own methods (Arabic: + // 18599/lyxsvn, Hebrew: e5f42f67d/lyxgit), some don't (Urdu, Syriac). + // Also note that the representation in the LyX file is probably wrong + // (see FIXME in TextMetrics::breakRow). + // Most likely, we should simply rely on Qt's unicode handling here. string const & lang = getFontSettings(bparams, pos).language()->lang(); - bool const arabic = lang == "arabic_arabtex" || lang == "arabic_arabi" - || lang == "farsi"; + + // With polyglossia, brackets and stuff need not be reversed in RTL scripts + // FIXME: The special casing for Hebrew parens is due to the special + // handling on input (for Hebrew in e5f42f67d/lyxgit); see #8251. char_type uc = c; + if (rp.use_polyglossia) { + switch (c) { + case '(': + if (lang == "hebrew") + uc = ')'; + break; + case ')': + if (lang == "hebrew") + uc = '('; + break; + } + return uc; + } + + // In the following languages, brackets don't need to be reversed. + // Furthermore, in arabic_arabi, they are transformed to Arabic + // Ornate Parentheses (dunno if this is really wanted) + bool const reversebrackets = lang != "arabic_arabtex" + && lang != "arabic_arabi" + && lang != "farsi"; + switch (c) { - case '(': - uc = arabic ? c : ')'; - break; - case ')': - uc = arabic ? c : '('; - break; case '[': - uc = ']'; + if (reversebrackets) + uc = ']'; break; case ']': - uc = '['; + if (reversebrackets) + uc = '['; break; case '{': uc = '}'; @@ -2295,13 +2383,13 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, case LYX_ALIGN_DECIMAL: break; case LYX_ALIGN_LEFT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") + if (!owner_->getParLanguage(bparams)->rightToLeft()) corrected_env(os, begin_tag, "flushleft", code, lastpar, column); else corrected_env(os, begin_tag, "flushright", code, lastpar, column); break; } case LYX_ALIGN_RIGHT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") + if (!owner_->getParLanguage(bparams)->rightToLeft()) corrected_env(os, begin_tag, "flushright", code, lastpar, column); else corrected_env(os, begin_tag, "flushleft", code, lastpar, column); @@ -2353,13 +2441,13 @@ bool Paragraph::Private::endTeXParParams(BufferParams const & bparams, case LYX_ALIGN_DECIMAL: break; case LYX_ALIGN_LEFT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") + if (!owner_->getParLanguage(bparams)->rightToLeft()) output = corrected_env(os, end_tag, "flushleft", code, lastpar, col); else output = corrected_env(os, end_tag, "flushright", code, lastpar, col); break; } case LYX_ALIGN_RIGHT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") + if (!owner_->getParLanguage(bparams)->rightToLeft()) output = corrected_env(os, end_tag, "flushright", code, lastpar, col); else output = corrected_env(os, end_tag, "flushleft", code, lastpar, col); @@ -2662,7 +2750,7 @@ void Paragraph::latex(BufferParams const & bparams, // latexSpecialChar ignores spaces if // style.pass_thru is false. if (i != body_pos - 1) { - if (d->simpleTeXBlanks(runparams, os, + if (d->simpleTeXBlanks(bparams, runparams, os, i, column, current_font, style)) { // A surrogate pair was output. We // must not call latexSpecialChar @@ -3324,7 +3412,8 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, retval += inset->xhtml(xs, np); } } else { - char_type c = getUChar(buf.masterBuffer()->params(), i); + char_type c = getUChar(buf.masterBuffer()->params(), + runparams, i); xs << c; } font_old = font.fontInfo(); @@ -3414,10 +3503,15 @@ bool Paragraph::isHardHyphenOrApostrophe(pos_type pos) const } -bool Paragraph::needsCProtection() const +bool Paragraph::needsCProtection(bool const fragile) const { - // first check the layout of the paragraph - if (layout().needcprotect) { + // first check the layout of the paragraph, but only in insets + InsetText const * textinset = inInset().asInsetText(); + bool const maintext = textinset + ? textinset->text().isMainText() + : false; + + if (!maintext && layout().needcprotect) { // Environments need cprotection regardless the content if (layout().latextype == LATEX_ENVIRONMENT) return true; @@ -3437,7 +3531,7 @@ bool Paragraph::needsCProtection() const // now check whether we have insets that need cprotection pos_type size = d->text_.size(); for (pos_type i = 0; i < size; ++i) - if (isInset(i) && getInset(i)->needsCProtection()) + if (isInset(i) && getInset(i)->needsCProtection(maintext, fragile)) return true; return false; @@ -4163,6 +4257,9 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, docstring word = asString(from, to, AS_STR_INSETS | AS_STR_SKIPDELETE); Language * lang = d->getSpellLanguage(from); + if (getFontSettings(d->inset_owner_->buffer().params(), from).fontInfo().nospellcheck() == FONT_ON) + return result; + wl = WordLangTuple(word, lang); if (word.empty())