X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=f2d97fe7c97cb9dbc60eac15e39e056391b14ec8;hb=7441172d4d9a26eb4824bb8bee003f457ef34f1c;hp=50971e3170c408b91e04abd6d0b3f39c02bed5c9;hpb=ef359f58b506c26255d8c992e7af0f9f2c6c3058;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 50971e3170..f2d97fe7c9 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -37,9 +37,10 @@ #include "OutputParams.h" #include "output_latex.h" #include "output_xhtml.h" +#include "output_docbook.h" #include "ParagraphParameters.h" #include "SpellChecker.h" -#include "sgml.h" +#include "xml.h" #include "texstream.h" #include "TextClass.h" #include "TexRow.h" @@ -63,6 +64,7 @@ #include "support/lassert.h" #include "support/lstrings.h" #include "support/textutils.h" +#include "output_docbook.h" #include #include @@ -82,14 +84,6 @@ using namespace lyx::support; namespace lyx { -namespace { - -/// Inset identifier (above 0x10ffff, for ucs-4) -char_type const META_INSET = 0x200001; - -} // namespace - - ///////////////////////////////////////////////////////////////////// // // SpellResultRange @@ -272,9 +266,9 @@ private: Ranges ranges_; /// the area of the paragraph with pending spell check FontSpan refresh_; - bool needs_refresh_; /// spell state cache version number SpellChecker::ChangeNumber current_change_number_; + bool needs_refresh_; void correctRangesAfterPos(pos_type pos, int offset) @@ -314,25 +308,21 @@ 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, Font const & font, Layout const & style); - /// 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(otexstream & os, docstring const & ltx, - Change const &, Encoding const &, pos_type & i); - /// This could go to ParagraphParameters if we want to. int startTeXParParams(BufferParams const &, otexstream &, OutputParams const &) const; @@ -360,7 +350,7 @@ public: BufferParams const & bparams, OutputParams const & runparams, Font const & running_font, - Change const & running_change, + string & alien_script, Layout const & style, pos_type & i, pos_type end_pos, @@ -449,10 +439,10 @@ public: return; } pos_type endpos = last; - owner_->locateWord(first, endpos, WHOLE_WORD); + owner_->locateWord(first, endpos, WHOLE_WORD, true); if (endpos < last) { endpos = last; - owner_->locateWord(last, endpos, WHOLE_WORD); + owner_->locateWord(last, endpos, WHOLE_WORD, true); } last = endpos; } @@ -462,7 +452,7 @@ public: { int numskips = 0; while (it != et && it->first < start) { - int skip = it->last - it->first + 1; + long skip = it->last - it->first + 1; start += skip; numskips += skip; ++it; @@ -489,9 +479,6 @@ public: /// FontList fontlist_; - /// - int id_; - /// ParagraphParameters params_; @@ -516,11 +503,13 @@ public: Layout const * layout_; /// SpellCheckerState speller_state_; + /// + int id_; }; Paragraph::Private::Private(Paragraph * owner, Layout const & layout) - : owner_(owner), inset_owner_(0), id_(-1), begin_of_body_(0), layout_(&layout) + : owner_(owner), inset_owner_(nullptr), begin_of_body_(0), layout_(&layout), id_(-1) { text_.reserve(100); } @@ -533,17 +522,16 @@ int Paragraph::Private::make_id() // LFUN_PARAGRAPH_GOTO to switch to a different buffer, for instance in the // outliner. // (thread-safe) - static atomic_uint next_id(0); + static int next_id(0); return next_id++; } Paragraph::Private::Private(Private const & p, Paragraph * owner) : owner_(owner), inset_owner_(p.inset_owner_), fontlist_(p.fontlist_), - id_(make_id()), params_(p.params_), changes_(p.changes_), insetlist_(p.insetlist_), begin_of_body_(p.begin_of_body_), text_(p.text_), words_(p.words_), - layout_(p.layout_) + layout_(p.layout_), id_(make_id()) { requestSpellCheck(p.text_.size()); } @@ -551,11 +539,11 @@ Paragraph::Private::Private(Private const & p, Paragraph * owner) Paragraph::Private::Private(Private const & p, Paragraph * owner, pos_type beg, pos_type end) - : owner_(owner), inset_owner_(p.inset_owner_), id_(make_id()), + : owner_(owner), inset_owner_(p.inset_owner_), params_(p.params_), changes_(p.changes_), insetlist_(p.insetlist_, beg, end), begin_of_body_(p.begin_of_body_), words_(p.words_), - layout_(p.layout_) + layout_(p.layout_), id_(make_id()) { if (beg >= pos_type(p.text_.size())) return; @@ -585,18 +573,6 @@ void Paragraph::addChangesToToc(DocIterator const & cdit, Buffer const & buf, } -void Paragraph::addChangesToBuffer(Buffer const & buf) const -{ - d->changes_.updateBuffer(buf); -} - - -bool Paragraph::isChangeUpdateRequired() const -{ - return d->changes_.isUpdateRequired(); -} - - bool Paragraph::isDeleted(pos_type start, pos_type end) const { LASSERT(start >= 0 && start <= size(), return false); @@ -614,6 +590,28 @@ bool Paragraph::isChanged(pos_type start, pos_type end) const return d->changes_.isChanged(start, end); } +// FIXME: Ideally the diverse isChanged() methods should account for that! +bool Paragraph::hasChangedInsets(pos_type start, pos_type end) const +{ + LASSERT(start >= 0 && start <= size(), return false); + LASSERT(end > start && end <= size() + 1, return false); + + for (auto const & icit : d->insetlist_) { + if (icit.pos < start) + continue; + if (icit.pos >= end) + break; + if (icit.inset && icit.inset->isChanged()) + return true; + } + return false; +} + +bool Paragraph::isChanged() const +{ + return d->changes_.isChanged(); +} + bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges) const { @@ -625,6 +623,11 @@ bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges) const return change.inserted() && change.currentAuthor(); } +Change Paragraph::parEndChange() const +{ + return d->changes_.lookup(size()); +} + void Paragraph::setChange(Change const & change) { @@ -659,8 +662,8 @@ void Paragraph::setChange(pos_type pos, Change const & change) // see comment in setChange(Change const &) above if (!change.deleted() && pos < size()) - if (Inset * inset = getInset(pos)) - inset->setChange(change); + if (Inset * inset = getInset(pos)) + inset->setChange(change); } @@ -864,9 +867,10 @@ int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges) return end - i; } - -int Paragraph::Private::latexSurrogatePair(otexstream & os, char_type c, - char_type next, OutputParams const & runparams) +// Handle combining characters +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 @@ -883,23 +887,22 @@ int Paragraph::Private::latexSurrogatePair(otexstream & os, char_type c, latex1 = from_ascii(tipashortcut); } } - docstring const latex2 = encoding.latexChar(c).first; - if (docstring(1, next) == latex1) { - // the encoding supports the combination + docstring latex2 = encoding.latexChar(c).first; + + if (bparams.useNonTeXFonts || docstring(1, next) == latex1) { + // Encoding supports the combination: + // output as is (combining char after base char). 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 << '}'; + } + + os << latex1 << "{" << latex2 << "}"; return latex1.length() + latex2.length() + 2; } -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, @@ -913,7 +916,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; } } @@ -944,86 +947,6 @@ bool Paragraph::Private::simpleTeXBlanks(OutputParams const & runparams, } -int Paragraph::Private::writeScriptChars(otexstream & os, - docstring const & ltx, - Change const & runningChange, - Encoding const & encoding, - pos_type & i) -{ - // FIXME: modifying i here is not very nice... - - // We only arrive here when character text_[i] could not be translated - // into the current latex encoding (or its latex translation has been forced,) - // and it belongs to a known script. - // TODO: We need \textcyr and \textgreek wrappers also for characters - // that can be encoded in the "LaTeX encoding" but not in the - // current *font encoding*. - // (See #9681 for details and test) - // Parameter ltx contains the latex translation of text_[i] as specified - // in the unicodesymbols file and is something like "\textXXX{}". - // The latex macro name "textXXX" specifies the script to which text_[i] - // belongs and we use it in order to check whether characters from the - // same script immediately follow, such that we can collect them in a - // single "\textXXX" macro. So, we have to retain "\textXXX{" - // for the first char but only "" for all subsequent chars. - docstring::size_type const brace1 = ltx.find_first_of(from_ascii("{")); - docstring::size_type const brace2 = ltx.find_last_of(from_ascii("}")); - string script = to_ascii(ltx.substr(1, brace1 - 1)); - int pos = 0; - int length = brace2; - bool closing_brace = true; - if (script == "textgreek" && encoding.latexName() == "iso-8859-7") { - // Correct encoding is being used, so we can avoid \textgreek. - // TODO: wrong test: we need to check the *font encoding* - // (i.e. the active language and its FontEncoding tag) - // instead of the LaTeX *input encoding*! - // See #9637 for details and test-cases. - pos = brace1 + 1; - length -= pos; - closing_brace = false; - } - os << ltx.substr(pos, length); - int size = text_.size(); - while (i + 1 < size) { - char_type const next = text_[i + 1]; - // Stop here if next character belongs to another script - // or there is a change in change tracking status. - if (!Encodings::isKnownScriptChar(next, script) || - runningChange != owner_->lookupChange(i + 1)) - break; - Font prev_font; - bool found = false; - FontList::const_iterator cit = fontlist_.begin(); - FontList::const_iterator end = fontlist_.end(); - for (; cit != end; ++cit) { - if (cit->pos() >= i && !found) { - prev_font = cit->font(); - found = true; - } - if (cit->pos() >= i + 1) - break; - } - // Stop here if there is a font attribute or encoding change. - if (found && cit != end && prev_font != cit->font()) - break; - docstring const latex = encoding.latexChar(next).first; - docstring::size_type const b1 = - latex.find_first_of(from_ascii("{")); - docstring::size_type const b2 = - latex.find_last_of(from_ascii("}")); - int const len = b2 - b1 - 1; - os << latex.substr(b1 + 1, len); - length += len; - ++i; - } - if (closing_brace) { - os << '}'; - ++length; - } - return length; -} - - void Paragraph::Private::latexInset(BufferParams const & bparams, otexstream & os, OutputParams & runparams, @@ -1089,14 +1012,16 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, bool close = false; odocstream::pos_type const len = os.os().tellp(); - if (inset->forceLTR() - && !runparams.use_polyglossia + if (inset->forceLTR(runparams) && running_font.isRightToLeft() // ERT is an exception, it should be output with no // decorations at all && inset->lyxCode() != ERT_CODE) { - if (running_font.language()->lang() == "farsi") - os << "\\beginL" << termcmd; + if (runparams.use_polyglossia) { + os << "\\LRE{"; + } else if (running_font.language()->lang() == "farsi" + || running_font.language()->lang() == "arabic_arabi") + os << "\\textLR{" << termcmd; else os << "\\L{"; close = true; @@ -1118,9 +1043,11 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, bool needPar = false; bool closeLanguage = arabtex || basefont.isRightToLeft() == running_font.isRightToLeft(); + // We pass non_inherit_inset = true here since size switches + // ought not to be terminated here (#8384). unsigned int count = running_font.latexWriteEndChanges(os, bparams, runparams, basefont, basefont, - needPar, closeLanguage); + needPar, closeLanguage, true); column += count; // if any font properties were closed, update the running_font, // making sure, however, to leave the language as it was @@ -1132,8 +1059,15 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, running_font = basefont; if (!closeLanguage) running_font.setLanguage(copy_font.language()); - // leave font open if language is still open - open_font = (running_font.language() == basefont.language()); + // For these, we use switches, so no need to close + basefont.fontInfo().setSize(copy_font.fontInfo().size()); + basefont.fontInfo().setFamily(copy_font.fontInfo().family()); + basefont.fontInfo().setSeries(copy_font.fontInfo().series()); + // leave font open if language or any of the switches is still open + open_font = (running_font.language() == basefont.language() + || running_font.fontInfo().size() == basefont.fontInfo().size() + || running_font.fontInfo().family() == basefont.fontInfo().family() + || running_font.fontInfo().series() == basefont.fontInfo().series()); if (closeLanguage) runparams.local_font = &basefont; } @@ -1152,12 +1086,8 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, throw(e); } - if (close) { - if (running_font.language()->lang() == "farsi") - os << "\\endL" << termcmd; - else - os << '}'; - } + if (close) + os << '}'; if (os.texrow().rows() > previous_row_count) { os.texrow().start(owner_->id(), i + 1); @@ -1175,16 +1105,13 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, BufferParams const & bparams, OutputParams const & runparams, Font const & running_font, - Change const & running_change, + string & alien_script, Layout const & style, pos_type & i, 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) @@ -1210,7 +1137,7 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, && !runparams.isFullUnicode() && bparams.main_font_encoding() == "T1" && latexSpecialT1(c, os, i, column)) return; - // NOTE: XeTeX and LuaTeX use EU1/2 (pre 2017) or TU (as of 2017) encoding + // NOTE: "fontspec" (non-TeX fonts) sets the font encoding to "TU" (untill 2017 "EU1" or "EU2") else if (!runparams.inIPA && !running_font.language()->internalFontEncoding() && runparams.isFullUnicode() && latexSpecialTU(c, os, i, column)) return; @@ -1315,17 +1242,16 @@ 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; } } - string script; pair latex = encoding.latexChar(c); docstring nextlatex; bool nexttipas = false; string nexttipashortcut; - if (next != '\0' && next != META_INSET && encoding.encodable(next)) { + if (next != '\0' && next != META_INSET && !encoding.encodable(next)) { nextlatex = encoding.latexChar(next).first; if (runparams.inIPA) { nexttipashortcut = Encodings::TIPAShortcut(next); @@ -1341,19 +1267,22 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, tipas = true; } } - if (Encodings::isKnownScriptChar(c, script) - && prefixIs(latex.first, from_ascii("\\" + script))) - column += writeScriptChars(os, latex.first, - running_change, encoding, i) - 1; - else if (latex.second + // eventually close "script wrapper" command (see `Paragraph::latex`) + if (!alien_script.empty() + && alien_script != Encodings::isKnownScriptChar(next)) { + column += latex.first.length(); + alien_script.clear(); + os << latex.first << "}"; + break; + } + if (latex.second && ((!prefixIs(nextlatex, '\\') && !prefixIs(nextlatex, '{') && !prefixIs(nextlatex, '}')) || (nexttipas && !prefixIs(from_ascii(nexttipashortcut), '\\'))) && !tipas) { - // Prevent eating of a following - // space or command corruption by + // Prevent eating of a following space or command corruption by // following characters if (next == ' ' || next == '\0') { column += latex.first.length() + 1; @@ -1438,7 +1367,8 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const otexstringstream os; os << layout_->preamble(); size_t const length = os.length(); - TeXOnePar(buf, buf.text(), buf.getParFromID(owner_->id()).pit(), os, + TeXOnePar(buf, *inset_owner_->getText(int(buf.getParFromID(owner_->id()).idx())), + buf.getParFromID(owner_->id()).pit(), os, features.runparams(), string(), 0, -1, true); if (os.length() > length) features.addPreambleSnippet(os.release(), true); @@ -1492,17 +1422,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); @@ -1694,7 +1661,11 @@ 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 (inInset().getLayout().isNeedProtect()) + fragile = true; + if (needsCProtection(fragile)) features.require("cprotect"); } @@ -1794,7 +1765,7 @@ Font const & Paragraph::getFontSettings(BufferParams const & bparams, // Optimisation: avoid a full font instantiation if there is no // language change from previous call. static Font previous_font; - static Language const * previous_lang = 0; + static Language const * previous_lang = nullptr; Language const * lang = getParLanguage(bparams); if (lang != previous_lang) { previous_lang = lang; @@ -1842,7 +1813,7 @@ Font const & Paragraph::getFirstFontSettings(BufferParams const & bparams) const // Optimisation: avoid a full font instantiation if there is no // language change from previous call. static Font previous_font; - static Language const * previous_lang = 0; + static Language const * previous_lang = nullptr; if (bparams.language != previous_lang) { previous_lang = bparams.language; previous_font = Font(inherit_font, bparams.language); @@ -1898,32 +1869,59 @@ 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]; - if (!getFontSettings(bparams, pos).isRightToLeft()) + + // Return unchanged character in LTR languages + // or if we use poylglossia/bidi (XeTeX). + if (rp.useBidiPackage() + || !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). + // Without polyglossia/bidi, we need to account for some special cases. + // FIXME This needs to be audited! + // Check if: + // * The input is as expected for all delimiters + // => checked for Hebrew! + // * The output matches the display in the LyX workarea + // => checked for Hebrew! + // * The special cases below are really necessary + // => checked for Hebrew! + // * In arabic_arabi, brackets are transformed to Arabic + // Ornate Parentheses. Is this is really wanted? + string const & lang = getFontSettings(bparams, pos).language()->lang(); - bool const arabic = lang == "arabic_arabtex" || lang == "arabic_arabi" - || lang == "farsi"; char_type uc = c; + + // 1. In the following languages, parentheses need to be reversed. + // Also with polyglodia/luabidi + bool const reverseparens = (lang == "hebrew" || rp.use_polyglossia); + + // 2. In the following languages, brackets don't need to be reversed. + bool const reversebrackets = lang != "arabic_arabtex" + && lang != "arabic_arabi" + && lang != "farsi"; + + // Now swap delimiters if needed. switch (c) { case '(': - uc = arabic ? c : ')'; + if (reverseparens) + uc = ')'; break; case ')': - uc = arabic ? c : '('; + if (reverseparens) + uc = '('; break; case '[': - uc = ']'; + if (reversebrackets) + uc = ']'; break; case ']': - uc = '['; + if (reversebrackets) + uc = '['; break; case '{': uc = '}'; @@ -2003,15 +2001,29 @@ depth_type Paragraph::getMaxDepthAfter() const } -LyXAlignment Paragraph::getAlign() const +LyXAlignment Paragraph::getAlign(BufferParams const & bparams) const { if (d->params_.align() == LYX_ALIGN_LAYOUT) - return d->layout_->align; + return getDefaultAlign(bparams); else return d->params_.align(); } +LyXAlignment Paragraph::getDefaultAlign(BufferParams const & bparams) const +{ + LyXAlignment res = layout().align; + if (isRTL(bparams)) { + // Swap sides + if (res == LYX_ALIGN_LEFT) + res = LYX_ALIGN_RIGHT; + else if (res == LYX_ALIGN_RIGHT) + res = LYX_ALIGN_LEFT; + } + return res; +} + + docstring const & Paragraph::labelString() const { return d->params_.labelString(); @@ -2042,13 +2054,6 @@ docstring Paragraph::expandLabel(Layout const & layout, } -docstring Paragraph::expandDocBookLabel(Layout const & layout, - BufferParams const & bparams) const -{ - return expandParagraphLabel(layout, bparams, false); -} - - docstring Paragraph::expandParagraphLabel(Layout const & layout, BufferParams const & bparams, bool process_appendix) const { @@ -2111,15 +2116,17 @@ void Paragraph::setBeginOfBody() // remove unnecessary getChar() calls pos_type i = 0; pos_type end = size(); - if (i < end && !(isNewline(i) || isEnvSeparator(i))) { + bool prev_char_deleted = false; + if (i < end && (!(isNewline(i) || isEnvSeparator(i)) || isDeleted(i))) { ++i; if (i < end) { char_type previous_char = d->text_[i]; if (!(isNewline(i) || isEnvSeparator(i))) { ++i; - while (i < end && previous_char != ' ') { + while (i < end && (previous_char != ' ' || prev_char_deleted)) { char_type temp = d->text_[i]; - if (isNewline(i) || isEnvSeparator(i)) + prev_char_deleted = isDeleted(i); + if (!isDeleted(i) && (isNewline(i) || isEnvSeparator(i))) break; ++i; previous_char = temp; @@ -2250,6 +2257,10 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, string const begin_tag = "\\begin"; InsetCode code = ownerCode(); bool const lastpar = runparams.isLastPar; + // RTL in classic (PDF)LaTeX (without the Bidi package) + // Luabibdi (used by LuaTeX) behaves like classic + bool const rtl_classic = owner_->getParLanguage(bparams)->rightToLeft() + && !runparams.useBidiPackage(); switch (curAlign) { case LYX_ALIGN_NONE: @@ -2259,16 +2270,18 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, case LYX_ALIGN_DECIMAL: break; case LYX_ALIGN_LEFT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") - corrected_env(os, begin_tag, "flushleft", code, lastpar, column); - else + if (rtl_classic) + // Classic (PDF)LaTeX switches the left/right logic in RTL mode corrected_env(os, begin_tag, "flushright", code, lastpar, column); + else + corrected_env(os, begin_tag, "flushleft", code, lastpar, column); break; } case LYX_ALIGN_RIGHT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") - corrected_env(os, begin_tag, "flushright", code, lastpar, column); - else + if (rtl_classic) + // Classic (PDF)LaTeX switches the left/right logic in RTL mode corrected_env(os, begin_tag, "flushleft", code, lastpar, column); + else + corrected_env(os, begin_tag, "flushright", code, lastpar, column); break; } case LYX_ALIGN_CENTER: { corrected_env(os, begin_tag, "center", code, lastpar, column); @@ -2308,6 +2321,10 @@ bool Paragraph::Private::endTeXParParams(BufferParams const & bparams, string const end_tag = "\\par\\end"; InsetCode code = ownerCode(); bool const lastpar = runparams.isLastPar; + // RTL in classic (PDF)LaTeX (without the Bidi package) + // Luabibdi (used by LuaTeX) behaves like classic + bool const rtl_classic = owner_->getParLanguage(bparams)->rightToLeft() + && !runparams.useBidiPackage(); switch (curAlign) { case LYX_ALIGN_NONE: @@ -2317,16 +2334,18 @@ bool Paragraph::Private::endTeXParParams(BufferParams const & bparams, case LYX_ALIGN_DECIMAL: break; case LYX_ALIGN_LEFT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") - output = corrected_env(os, end_tag, "flushleft", code, lastpar, col); - else + if (rtl_classic) + // Classic (PDF)LaTeX switches the left/right logic in RTL mode output = corrected_env(os, end_tag, "flushright", code, lastpar, col); + else + output = corrected_env(os, end_tag, "flushleft", code, lastpar, col); break; } case LYX_ALIGN_RIGHT: { - if (owner_->getParLanguage(bparams)->babel() != "hebrew") - output = corrected_env(os, end_tag, "flushright", code, lastpar, col); - else + if (rtl_classic) + // Classic (PDF)LaTeX switches the left/right logic in RTL mode 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_CENTER: { corrected_env(os, end_tag, "center", code, lastpar, col); @@ -2365,10 +2384,20 @@ void Paragraph::latex(BufferParams const & bparams, // of the body. Font basefont; + // If there is an open font-encoding changing command (script wrapper), + // alien_script is set to its name + string alien_script; + string script; + // Maybe we have to create a optional argument. pos_type body_pos = beginOfBody(); unsigned int column = 0; + // If we are inside an non inheritFont() inset, the real outerfont is local_font + Font const real_outerfont = (!inInset().inheritFont() + && runparams.local_font != nullptr) + ? Font(runparams.local_font->fontInfo()) : outerfont; + if (body_pos > 0) { // the optional argument is kept in curly brackets in // case it contains a ']' @@ -2378,9 +2407,9 @@ void Paragraph::latex(BufferParams const & bparams, // braces when it parses \item. os << "[{"; column += 2; - basefont = getLabelFont(bparams, outerfont); + basefont = getLabelFont(bparams, real_outerfont); } else { - basefont = getLayoutFont(bparams, outerfont); + basefont = getLayoutFont(bparams, real_outerfont); } // Which font is currently active? @@ -2425,7 +2454,7 @@ void Paragraph::latex(BufferParams const & bparams, basefont, basefont, needPar); open_font = false; } - basefont = getLayoutFont(bparams, outerfont); + basefont = getLayoutFont(bparams, real_outerfont); running_font = basefont; column += Changes::latexMarkChange(os, bparams, @@ -2459,9 +2488,14 @@ void Paragraph::latex(BufferParams const & bparams, Change const & change = runparams.inDeletedInset ? runparams.changeOfDeletedInset : lookupChange(i); + char_type const c = d->text_[i]; + // Check whether a display math inset follows - if (d->text_[i] == META_INSET + if (c == META_INSET && i >= start_pos && (end_pos == -1 || i < end_pos)) { + if (isDeleted(i)) + runparams.ctObject = getInset(i)->CtObject(runparams); + InsetMath const * im = getInset(i)->asInsetMath(); if (im && im->asHullInset() && im->asHullInset()->outerDisplay()) { @@ -2487,7 +2521,8 @@ void Paragraph::latex(BufferParams const & bparams, basefont, needPar); open_font = false; } - basefont = getLayoutFont(bparams, outerfont); + basefont = (body_pos > i) ? getLabelFont(bparams, real_outerfont) + : getLayoutFont(bparams, real_outerfont); running_font = basefont; column += Changes::latexMarkChange(os, bparams, Change(Change::INSERTED), change, rp); @@ -2495,6 +2530,11 @@ void Paragraph::latex(BufferParams const & bparams, } if (bparams.output_changes && runningChange != change) { + if (!alien_script.empty()) { + column += 1; + os << "}"; + alien_script.clear(); + } if (open_font) { bool needPar = false; column += running_font.latexWriteEndChanges( @@ -2502,7 +2542,8 @@ void Paragraph::latex(BufferParams const & bparams, basefont, basefont, needPar); open_font = false; } - basefont = getLayoutFont(bparams, outerfont); + basefont = (body_pos > i) ? getLabelFont(bparams, real_outerfont) + : getLayoutFont(bparams, real_outerfont); running_font = basefont; column += Changes::latexMarkChange(os, bparams, runningChange, change, runparams); @@ -2521,38 +2562,55 @@ void Paragraph::latex(BufferParams const & bparams, Font const current_font = getFont(bparams, i, outerfont); Font const last_font = running_font; + bool const in_ct_deletion = (bparams.output_changes + && runningChange == change + && change.type == Change::DELETED + && !os.afterParbreak()); // Do we need to close the previous font? if (open_font && (current_font != running_font || current_font.language() != running_font.language())) { + // ensure there is no open script-wrapper + if (!alien_script.empty()) { + column += 1; + os << "}"; + alien_script.clear(); + } bool needPar = false; + if (in_ct_deletion) { + // We have to close and then reopen \lyxdeleted, + // as strikeout needs to be on lowest level. + os << '}'; + column += 1; + } column += running_font.latexWriteEndChanges( os, bparams, runparams, basefont, (i == body_pos-1) ? basefont : current_font, needPar); + if (in_ct_deletion) { + // We have to close and then reopen \lyxdeleted, + // as strikeout needs to be on lowest level. + OutputParams rp = runparams; + column += Changes::latexMarkChange(os, bparams, + Change(Change::UNCHANGED), Change(Change::DELETED), rp); + } running_font = basefont; 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. - string const lang_end_command = runparams.use_polyglossia ? - "\\end{$$lang}" : lyxrc.language_command_end; - bool const using_begin_end = runparams.use_polyglossia || - !lang_end_command.empty(); - if (!running_lang.empty() && - (!using_begin_end || running_lang == openLanguageName()) && - current_font.language()->encoding()->package() == Encoding::CJK) { - string end_tag = subst(lang_end_command, - "$$lang", - running_lang); - os << from_ascii(end_tag); - column += end_tag.length(); - if (using_begin_end) - popLanguageName(); + // if necessary, close language environment before opening CJK + string const running_lang = running_font.language()->babel(); + string const lang_end_command = lyxrc.language_command_end; + if (!lang_end_command.empty() && !bparams.useNonTeXFonts + && !running_lang.empty() + && running_lang == openLanguageName() + && current_font.language()->encoding()->package() == Encoding::CJK) { + string end_tag = subst(lang_end_command, "$$lang", running_lang); + os << from_ascii(end_tag); + column += end_tag.length(); + popLanguageName(); } // Switch file encoding if necessary (and allowed) @@ -2568,8 +2626,6 @@ void Paragraph::latex(BufferParams const & bparams, } } - char_type const c = d->text_[i]; - // A display math inset inside an ulem command will be output // as a box of width \linewidth, so we have to either disable // indentation if the inset starts a paragraph, or start a new @@ -2585,13 +2641,25 @@ void Paragraph::latex(BufferParams const & bparams, // Do we need to change font? if ((current_font != running_font || - current_font.language() != running_font.language()) && - i != body_pos - 1) + current_font.language() != running_font.language()) + && i != body_pos - 1) { - odocstringstream ods; - column += current_font.latexWriteStartChanges(ods, bparams, + if (in_ct_deletion) { + // We have to close and then reopen \lyxdeleted, + // as strikeout needs to be on lowest level. + bool needPar = false; + OutputParams rp = runparams; + column += running_font.latexWriteEndChanges( + os, bparams, rp, basefont, + basefont, needPar); + os << '}'; + column += 1; + } + otexstringstream ots; + bool const non_inherit_inset = (c == META_INSET && getInset(i) && !getInset(i)->inheritFont()); + column += current_font.latexWriteStartChanges(ots, bparams, runparams, basefont, - last_font); + last_font, non_inherit_inset); // Check again for display math in ulem commands as a // font change may also occur just before a math inset. if (runparams.inDisplayMath && !deleted_display_math @@ -2603,19 +2671,24 @@ void Paragraph::latex(BufferParams const & bparams, } running_font = current_font; open_font = true; - docstring fontchange = ods.str(); + docstring fontchange = ots.str(); + os << fontchange; // check whether the fontchange ends with a \\textcolor - // modifier and the text starts with a space (bug 4473) + // modifier and the text starts with a space. If so we + // need to add } in order to prevent \\textcolor from gobbling + // the space (bug 4473). docstring const last_modifier = rsplit(fontchange, '\\'); if (prefixIs(last_modifier, from_ascii("textcolor")) && c == ' ') - os << fontchange << from_ascii("{}"); - // check if the fontchange ends with a trailing blank - // (like "\small " (see bug 3382) - else if (suffixIs(fontchange, ' ') && c == ' ') - os << fontchange.substr(0, fontchange.size() - 1) - << from_ascii("{}"); - else - os << fontchange; + os << from_ascii("{}"); + else if (ots.terminateCommand()) + os << termcmd; + if (in_ct_deletion) { + // We have to close and then reopen \lyxdeleted, + // as strikeout needs to be on lowest level. + OutputParams rp = runparams; + column += Changes::latexMarkChange(os, bparams, + Change(Change::UNCHANGED), change, rp); + } } // FIXME: think about end_pos implementation... @@ -2626,7 +2699,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 @@ -2657,7 +2730,7 @@ void Paragraph::latex(BufferParams const & bparams, Inset const * inset = getInset(i); InsetText const * textinset = inset ? inset->asInsetText() - : 0; + : nullptr; if (i + 1 == size() && textinset && !inset->getLayout().isDisplay()) { ParagraphList const & pars = @@ -2667,10 +2740,10 @@ void Paragraph::latex(BufferParams const & bparams, pit < 0 || pars[pit].empty() ? pars[pit].getLayoutFont( bparams, - outerfont) + real_outerfont) : pars[pit].getFont(bparams, pars[pit].size() - 1, - outerfont); + real_outerfont); if (lastfont.fontInfo().size() != basefont.fontInfo().size()) { ++parInline; @@ -2678,16 +2751,40 @@ void Paragraph::latex(BufferParams const & bparams, } } d->latexInset(bparams, os, rp, running_font, - basefont, outerfont, open_font, + basefont, real_outerfont, open_font, runningChange, style, i, column); if (incremented) --parInline; + + if (runparams.ctObject == OutputParams::CT_DISPLAYOBJECT + || runparams.ctObject == OutputParams::CT_UDISPLAYOBJECT) { + // Close \lyx*deleted and force its + // reopening (if needed) + os << '}'; + column++; + runningChange = Change(Change::UNCHANGED); + runparams.ctObject = OutputParams::CT_NORMAL; + } } } else if (i >= start_pos && (end_pos == -1 || i < end_pos)) { + if (!bparams.useNonTeXFonts) + script = Encodings::isKnownScriptChar(c); + if (script != alien_script) { + if (!alien_script.empty()) { + os << "}"; + alien_script.clear(); + } + string fontenc = running_font.language()->fontenc(bparams); + if (!script.empty() + && !Encodings::fontencSupportsScript(fontenc, script)) { + column += script.length() + 2; + os << "\\" << script << "{"; + alien_script = script; + } + } try { - d->latexSpecialChar(os, bparams, rp, - running_font, runningChange, - style, i, end_pos, column); + d->latexSpecialChar(os, bparams, rp, running_font, + alien_script, style, i, end_pos, column); } catch (EncodingException & e) { if (runparams.dryrun) { os << "<" << _("LyX Warning: ") @@ -2711,6 +2808,19 @@ void Paragraph::latex(BufferParams const & bparams, // such as Note that do not produce any output, so that no // command is ever executed but its opening was recorded. runparams.inulemcmd = rp.inulemcmd; + + // These need to be passed upstream as well + runparams.need_maketitle = rp.need_maketitle; + runparams.have_maketitle = rp.have_maketitle; + + // And finally, pass the post_macros upstream + runparams.post_macro = rp.post_macro; + } + + // Close wrapper for alien script + if (!alien_script.empty()) { + os << "}"; + alien_script.clear(); } // If we have an open font definition, we have to close it @@ -2726,8 +2836,8 @@ void Paragraph::latex(BufferParams const & bparams, // since this produces unwanted whitespace. Font const font = empty() - ? getLayoutFont(bparams, outerfont) - : getFont(bparams, size() - 1, outerfont); + ? getLayoutFont(bparams, real_outerfont) + : getFont(bparams, size() - 1, real_outerfont); InsetText const * textinset = inInset().asInsetText(); @@ -2822,7 +2932,7 @@ bool Paragraph::emptyTag() const } -string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) +string Paragraph::getID(Buffer const &, OutputParams const &) const { for (pos_type i = 0; i < size(); ++i) { @@ -2831,7 +2941,7 @@ string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) if (lyx_code == LABEL_CODE) { InsetLabel const * const il = static_cast(inset); docstring const & id = il->getParam("name"); - return "id='" + to_utf8(sgml::cleanID(buf, runparams, id)) + "'"; + return "id='" + to_utf8(xml::cleanID(id)) + "'"; } } } @@ -2839,25 +2949,24 @@ string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) } -pos_type Paragraph::firstWordDocBook(odocstream & os, OutputParams const & runparams) - const +pos_type Paragraph::firstWordDocBook(XMLStream & xs, OutputParams const & runparams) const { pos_type i; for (i = 0; i < size(); ++i) { if (Inset const * inset = getInset(i)) { - inset->docbook(os, runparams); + inset->docbook(xs, runparams); } else { char_type c = d->text_[i]; if (c == ' ') break; - os << sgml::escapeChar(c); + xs << c; } } return i; } -pos_type Paragraph::firstWordLyXHTML(XHTMLStream & xs, OutputParams const & runparams) +pos_type Paragraph::firstWordLyXHTML(XMLStream & xs, OutputParams const & runparams) const { pos_type i; @@ -2892,78 +3001,354 @@ bool Paragraph::Private::onlyText(Buffer const & buf, Font const & outerfont, po } -void Paragraph::simpleDocBookOnePar(Buffer const & buf, - odocstream & os, - OutputParams const & runparams, - Font const & outerfont, - pos_type initial) const +namespace { + +void doFontSwitchDocBook(vector & tagsToOpen, + vector & tagsToClose, + bool & flag, FontState curstate, xml::FontTypes type) { + if (curstate == FONT_ON) { + tagsToOpen.push_back(docbookStartFontTag(type)); + flag = true; + } else if (flag) { + tagsToClose.push_back(docbookEndFontTag(type)); + flag = false; + } +} + +class OptionalFontType { +public: + xml::FontTypes ft; + bool has_value; + + OptionalFontType(): ft(xml::FT_EMPH), has_value(false) {} // A possible value at random for ft. + OptionalFontType(xml::FontTypes ft): ft(ft), has_value(true) {} +}; + +OptionalFontType fontShapeToXml(FontShape fs) +{ + switch (fs) { + case ITALIC_SHAPE: + return {xml::FT_ITALIC}; + case SLANTED_SHAPE: + return {xml::FT_SLANTED}; + case SMALLCAPS_SHAPE: + return {xml::FT_SMALLCAPS}; + case UP_SHAPE: + case INHERIT_SHAPE: + return {}; + default: + // the other tags are for internal use + LATTEST(false); + return {}; + } +} + +OptionalFontType fontFamilyToXml(FontFamily fm) +{ + switch (fm) { + case ROMAN_FAMILY: + return {xml::FT_ROMAN}; + case SANS_FAMILY: + return {xml::FT_SANS}; + case TYPEWRITER_FAMILY: + return {xml::FT_TYPE}; + case INHERIT_FAMILY: + return {}; + default: + // the other tags are for internal use + LATTEST(false); + return {}; + } +} + +OptionalFontType fontSizeToXml(FontSize fs) +{ + switch (fs) { + case TINY_SIZE: + return {xml::FT_SIZE_TINY}; + case SCRIPT_SIZE: + return {xml::FT_SIZE_SCRIPT}; + case FOOTNOTE_SIZE: + return {xml::FT_SIZE_FOOTNOTE}; + case SMALL_SIZE: + return {xml::FT_SIZE_SMALL}; + case LARGE_SIZE: + return {xml::FT_SIZE_LARGE}; + case LARGER_SIZE: + return {xml::FT_SIZE_LARGER}; + case LARGEST_SIZE: + return {xml::FT_SIZE_LARGEST}; + case HUGE_SIZE: + return {xml::FT_SIZE_HUGE}; + case HUGER_SIZE: + return {xml::FT_SIZE_HUGER}; + case INCREASE_SIZE: + return {xml::FT_SIZE_INCREASE}; + case DECREASE_SIZE: + return {xml::FT_SIZE_DECREASE}; + case INHERIT_SIZE: + case NORMAL_SIZE: + return {}; + default: + // the other tags are for internal use + LATTEST(false); + return {}; + } +} + +struct DocBookFontState +{ + FontShape curr_fs = INHERIT_SHAPE; + FontFamily curr_fam = INHERIT_FAMILY; + FontSize curr_size = INHERIT_SIZE; + + // track whether we have opened these tags bool emph_flag = false; + bool bold_flag = false; + bool noun_flag = false; + bool ubar_flag = false; + bool dbar_flag = false; + bool sout_flag = false; + bool xout_flag = false; + bool wave_flag = false; + // shape tags + bool shap_flag = false; + // family tags + bool faml_flag = false; + // size tags + bool size_flag = false; +}; + +std::tuple, vector> computeDocBookFontSwitch(FontInfo const & font_old, + Font const & font, + std::string const & default_family, + DocBookFontState & fs) +{ + vector tagsToOpen; + vector tagsToClose; + + // emphasis + FontState curstate = font.fontInfo().emph(); + if (font_old.emph() != curstate) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.emph_flag, curstate, xml::FT_EMPH); + + // noun + curstate = font.fontInfo().noun(); + if (font_old.noun() != curstate) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.noun_flag, curstate, xml::FT_NOUN); + + // underbar + curstate = font.fontInfo().underbar(); + if (font_old.underbar() != curstate) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.ubar_flag, curstate, xml::FT_UBAR); + + // strikeout + curstate = font.fontInfo().strikeout(); + if (font_old.strikeout() != curstate) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.sout_flag, curstate, xml::FT_SOUT); + + // xout + curstate = font.fontInfo().xout(); + if (font_old.xout() != curstate) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.xout_flag, curstate, xml::FT_XOUT); + + // double underbar + curstate = font.fontInfo().uuline(); + if (font_old.uuline() != curstate) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.dbar_flag, curstate, xml::FT_DBAR); + + // wavy line + curstate = font.fontInfo().uwave(); + if (font_old.uwave() != curstate) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.wave_flag, curstate, xml::FT_WAVE); + + // bold + // a little hackish, but allows us to reuse what we have. + curstate = (font.fontInfo().series() == BOLD_SERIES ? FONT_ON : FONT_OFF); + if (font_old.series() != font.fontInfo().series()) + doFontSwitchDocBook(tagsToOpen, tagsToClose, fs.bold_flag, curstate, xml::FT_BOLD); + + // Font shape + fs.curr_fs = font.fontInfo().shape(); + FontShape old_fs = font_old.shape(); + if (old_fs != fs.curr_fs) { + if (fs.shap_flag) { + OptionalFontType tag = fontShapeToXml(old_fs); + if (tag.has_value) + tagsToClose.push_back(docbookEndFontTag(tag.ft)); + fs.shap_flag = false; + } + + OptionalFontType tag = fontShapeToXml(fs.curr_fs); + if (tag.has_value) + tagsToOpen.push_back(docbookStartFontTag(tag.ft)); + } + + // Font family + fs.curr_fam = font.fontInfo().family(); + FontFamily old_fam = font_old.family(); + if (old_fam != fs.curr_fam) { + if (fs.faml_flag) { + OptionalFontType tag = fontFamilyToXml(old_fam); + if (tag.has_value) + tagsToClose.push_back(docbookEndFontTag(tag.ft)); + fs.faml_flag = false; + } + switch (fs.curr_fam) { + case ROMAN_FAMILY: + // we will treat a "default" font family as roman, since we have + // no other idea what to do. + if (default_family != "rmdefault" && default_family != "default") { + tagsToOpen.push_back(docbookStartFontTag(xml::FT_ROMAN)); + fs.faml_flag = true; + } + break; + case SANS_FAMILY: + if (default_family != "sfdefault") { + tagsToOpen.push_back(docbookStartFontTag(xml::FT_SANS)); + fs.faml_flag = true; + } + break; + case TYPEWRITER_FAMILY: + if (default_family != "ttdefault") { + tagsToOpen.push_back(docbookStartFontTag(xml::FT_TYPE)); + fs.faml_flag = true; + } + break; + case INHERIT_FAMILY: + break; + default: + // the other tags are for internal use + LATTEST(false); + break; + } + } + + // Font size + fs.curr_size = font.fontInfo().size(); + FontSize old_size = font_old.size(); + if (old_size != fs.curr_size) { + if (fs.size_flag) { + OptionalFontType tag = fontSizeToXml(old_size); + if (tag.has_value) + tagsToClose.push_back(docbookEndFontTag(tag.ft)); + fs.size_flag = false; + } + + OptionalFontType tag = fontSizeToXml(fs.curr_size); + if (tag.has_value) { + tagsToOpen.push_back(docbookStartFontTag(tag.ft)); + fs.size_flag = true; + } + } + + return std::tuple, vector>(tagsToOpen, tagsToClose); +} + +} // anonymous namespace + + +void Paragraph::simpleDocBookOnePar(Buffer const & buf, + XMLStream & xs, + OutputParams const & runparams, + Font const & outerfont, + bool start_paragraph, bool close_paragraph, + pos_type initial) const +{ + // track whether we have opened these tags + DocBookFontState fs; + + if (start_paragraph) + xs.startDivision(allowEmpty()); Layout const & style = *d->layout_; FontInfo font_old = - style.labeltype == LABEL_MANUAL ? style.labelfont : style.font; + style.labeltype == LABEL_MANUAL ? style.labelfont : style.font; - if (style.pass_thru && !d->onlyText(buf, outerfont, initial)) - os << "]]>"; + string const default_family = + buf.masterBuffer()->params().fonts_default_family; + + vector tagsToOpen; + vector tagsToClose; // parsing main loop for (pos_type i = initial; i < size(); ++i) { - Font font = getFont(buf.params(), i, outerfont); - - // handle tag - if (font_old.emph() != font.fontInfo().emph()) { - if (font.fontInfo().emph() == FONT_ON) { - os << ""; - emph_flag = true; - } else if (i != initial) { - os << ""; - emph_flag = false; - } - } + // let's not show deleted material in the output + if (isDeleted(i)) + continue; + + Font const font = getFont(buf.masterBuffer()->params(), i, outerfont); + + // Determine which tags should be opened or closed. + tie(tagsToOpen, tagsToClose) = computeDocBookFontSwitch(font_old, font, default_family, fs); + + // FIXME XHTML + // Other such tags? What about the other text ranges? + + vector::const_iterator cit = tagsToClose.begin(); + vector::const_iterator cen = tagsToClose.end(); + for (; cit != cen; ++cit) + xs << *cit; + + vector::const_iterator sit = tagsToOpen.begin(); + vector::const_iterator sen = tagsToOpen.end(); + for (; sit != sen; ++sit) + xs << *sit; + + tagsToClose.clear(); + tagsToOpen.clear(); if (Inset const * inset = getInset(i)) { - inset->docbook(os, runparams); + if (!runparams.for_toc || inset->isInToc()) { + OutputParams np = runparams; + np.local_font = &font; + // If the paragraph has size 1, then we are in the "special + // case" where we do not output the containing paragraph info. + // This "special case" is defined in more details in output_docbook.cpp, makeParagraphs. The results + // of that brittle logic is passed to this function through open_par. + if (!inset->getLayout().htmlisblock() && size() != 1) // TODO: htmlisblock here too! + np.docbook_in_par = true; + inset->docbook(xs, np); + } } else { - char_type c = d->text_[i]; - - if (style.pass_thru) - os.put(c); - else - os << sgml::escapeChar(c); + char_type c = getUChar(buf.masterBuffer()->params(), runparams, i); + xs << c; } font_old = font.fontInfo(); } - if (emph_flag) { - os << ""; - } - - if (style.free_spacing) - os << '\n'; - if (style.pass_thru && !d->onlyText(buf, outerfont, initial)) - os << " & tagsToOpen, - vector & tagsToClose, - bool & flag, FontState curstate, html::FontTypes type) + +void doFontSwitchXHTML(vector & tagsToOpen, + vector & tagsToClose, + bool & flag, FontState curstate, xml::FontTypes type) { if (curstate == FONT_ON) { - tagsToOpen.push_back(html::FontTag(type)); + tagsToOpen.push_back(xhtmlStartFontTag(type)); flag = true; } else if (flag) { - tagsToClose.push_back(html::EndFontTag(type)); + tagsToClose.push_back(xhtmlEndFontTag(type)); flag = false; } } -} // namespace + +} // anonymous namespace docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, - XHTMLStream & xs, + XMLStream & xs, OutputParams const & runparams, Font const & outerfont, bool start_paragraph, bool close_paragraph, @@ -2997,13 +3382,13 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, FontShape curr_fs = INHERIT_SHAPE; FontFamily curr_fam = INHERIT_FAMILY; - FontSize curr_size = FONT_SIZE_INHERIT; + FontSize curr_size = INHERIT_SIZE; string const default_family = buf.masterBuffer()->params().fonts_default_family; - vector tagsToOpen; - vector tagsToClose; + vector tagsToOpen; + vector tagsToClose; // parsing main loop for (pos_type i = initial; i < size(); ++i) { @@ -3016,43 +3401,43 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, // emphasis FontState curstate = font.fontInfo().emph(); if (font_old.emph() != curstate) - doFontSwitch(tagsToOpen, tagsToClose, emph_flag, curstate, html::FT_EMPH); + doFontSwitchXHTML(tagsToOpen, tagsToClose, emph_flag, curstate, xml::FT_EMPH); // noun curstate = font.fontInfo().noun(); if (font_old.noun() != curstate) - doFontSwitch(tagsToOpen, tagsToClose, noun_flag, curstate, html::FT_NOUN); + doFontSwitchXHTML(tagsToOpen, tagsToClose, noun_flag, curstate, xml::FT_NOUN); // underbar curstate = font.fontInfo().underbar(); if (font_old.underbar() != curstate) - doFontSwitch(tagsToOpen, tagsToClose, ubar_flag, curstate, html::FT_UBAR); + doFontSwitchXHTML(tagsToOpen, tagsToClose, ubar_flag, curstate, xml::FT_UBAR); // strikeout curstate = font.fontInfo().strikeout(); if (font_old.strikeout() != curstate) - doFontSwitch(tagsToOpen, tagsToClose, sout_flag, curstate, html::FT_SOUT); + doFontSwitchXHTML(tagsToOpen, tagsToClose, sout_flag, curstate, xml::FT_SOUT); // xout curstate = font.fontInfo().xout(); if (font_old.xout() != curstate) - doFontSwitch(tagsToOpen, tagsToClose, xout_flag, curstate, html::FT_XOUT); + doFontSwitchXHTML(tagsToOpen, tagsToClose, xout_flag, curstate, xml::FT_XOUT); // double underbar curstate = font.fontInfo().uuline(); if (font_old.uuline() != curstate) - doFontSwitch(tagsToOpen, tagsToClose, dbar_flag, curstate, html::FT_DBAR); + doFontSwitchXHTML(tagsToOpen, tagsToClose, dbar_flag, curstate, xml::FT_DBAR); // wavy line curstate = font.fontInfo().uwave(); if (font_old.uwave() != curstate) - doFontSwitch(tagsToOpen, tagsToClose, wave_flag, curstate, html::FT_WAVE); + doFontSwitchXHTML(tagsToOpen, tagsToClose, wave_flag, curstate, xml::FT_WAVE); // bold // a little hackish, but allows us to reuse what we have. curstate = (font.fontInfo().series() == BOLD_SERIES ? FONT_ON : FONT_OFF); if (font_old.series() != font.fontInfo().series()) - doFontSwitch(tagsToOpen, tagsToClose, bold_flag, curstate, html::FT_BOLD); + doFontSwitchXHTML(tagsToOpen, tagsToClose, bold_flag, curstate, xml::FT_BOLD); // Font shape curr_fs = font.fontInfo().shape(); @@ -3061,13 +3446,13 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, if (shap_flag) { switch (old_fs) { case ITALIC_SHAPE: - tagsToClose.push_back(html::EndFontTag(html::FT_ITALIC)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_ITALIC)); break; case SLANTED_SHAPE: - tagsToClose.push_back(html::EndFontTag(html::FT_SLANTED)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SLANTED)); break; case SMALLCAPS_SHAPE: - tagsToClose.push_back(html::EndFontTag(html::FT_SMALLCAPS)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SMALLCAPS)); break; case UP_SHAPE: case INHERIT_SHAPE: @@ -3081,15 +3466,15 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, } switch (curr_fs) { case ITALIC_SHAPE: - tagsToOpen.push_back(html::FontTag(html::FT_ITALIC)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_ITALIC)); shap_flag = true; break; case SLANTED_SHAPE: - tagsToOpen.push_back(html::FontTag(html::FT_SLANTED)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SLANTED)); shap_flag = true; break; case SMALLCAPS_SHAPE: - tagsToOpen.push_back(html::FontTag(html::FT_SMALLCAPS)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SMALLCAPS)); shap_flag = true; break; case UP_SHAPE: @@ -3109,13 +3494,13 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, if (faml_flag) { switch (old_fam) { case ROMAN_FAMILY: - tagsToClose.push_back(html::EndFontTag(html::FT_ROMAN)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_ROMAN)); break; case SANS_FAMILY: - tagsToClose.push_back(html::EndFontTag(html::FT_SANS)); - break; + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SANS)); + break; case TYPEWRITER_FAMILY: - tagsToClose.push_back(html::EndFontTag(html::FT_TYPE)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_TYPE)); break; case INHERIT_FAMILY: break; @@ -3131,19 +3516,19 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, // we will treat a "default" font family as roman, since we have // no other idea what to do. if (default_family != "rmdefault" && default_family != "default") { - tagsToOpen.push_back(html::FontTag(html::FT_ROMAN)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_ROMAN)); faml_flag = true; } break; case SANS_FAMILY: if (default_family != "sfdefault") { - tagsToOpen.push_back(html::FontTag(html::FT_SANS)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SANS)); faml_flag = true; } break; case TYPEWRITER_FAMILY: if (default_family != "ttdefault") { - tagsToOpen.push_back(html::FontTag(html::FT_TYPE)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_TYPE)); faml_flag = true; } break; @@ -3162,41 +3547,41 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, if (old_size != curr_size) { if (size_flag) { switch (old_size) { - case FONT_SIZE_TINY: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_TINY)); + case TINY_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_TINY)); break; - case FONT_SIZE_SCRIPT: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_SCRIPT)); + case SCRIPT_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_SCRIPT)); break; - case FONT_SIZE_FOOTNOTE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_FOOTNOTE)); + case FOOTNOTE_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_FOOTNOTE)); break; - case FONT_SIZE_SMALL: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_SMALL)); + case SMALL_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_SMALL)); break; - case FONT_SIZE_LARGE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_LARGE)); + case LARGE_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_LARGE)); break; - case FONT_SIZE_LARGER: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_LARGER)); + case LARGER_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_LARGER)); break; - case FONT_SIZE_LARGEST: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_LARGEST)); + case LARGEST_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_LARGEST)); break; - case FONT_SIZE_HUGE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_HUGE)); + case HUGE_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_HUGE)); break; - case FONT_SIZE_HUGER: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_HUGER)); + case HUGER_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_HUGER)); break; - case FONT_SIZE_INCREASE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_INCREASE)); + case INCREASE_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_INCREASE)); break; - case FONT_SIZE_DECREASE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_DECREASE)); + case DECREASE_SIZE: + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_DECREASE)); break; - case FONT_SIZE_INHERIT: - case FONT_SIZE_NORMAL: + case INHERIT_SIZE: + case NORMAL_SIZE: break; default: // the other tags are for internal use @@ -3206,52 +3591,52 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, size_flag = false; } switch (curr_size) { - case FONT_SIZE_TINY: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_TINY)); + case TINY_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_TINY)); size_flag = true; break; - case FONT_SIZE_SCRIPT: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_SCRIPT)); + case SCRIPT_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_SCRIPT)); size_flag = true; break; - case FONT_SIZE_FOOTNOTE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_FOOTNOTE)); + case FOOTNOTE_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_FOOTNOTE)); size_flag = true; break; - case FONT_SIZE_SMALL: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_SMALL)); + case SMALL_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_SMALL)); size_flag = true; break; - case FONT_SIZE_LARGE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_LARGE)); + case LARGE_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_LARGE)); size_flag = true; break; - case FONT_SIZE_LARGER: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_LARGER)); + case LARGER_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_LARGER)); size_flag = true; break; - case FONT_SIZE_LARGEST: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_LARGEST)); + case LARGEST_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_LARGEST)); size_flag = true; break; - case FONT_SIZE_HUGE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_HUGE)); + case HUGE_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_HUGE)); size_flag = true; break; - case FONT_SIZE_HUGER: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_HUGER)); + case HUGER_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_HUGER)); size_flag = true; break; - case FONT_SIZE_INCREASE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_INCREASE)); + case INCREASE_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_INCREASE)); size_flag = true; break; - case FONT_SIZE_DECREASE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_DECREASE)); + case DECREASE_SIZE: + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_DECREASE)); size_flag = true; break; - case FONT_SIZE_NORMAL: - case FONT_SIZE_INHERIT: + case INHERIT_SIZE: + case NORMAL_SIZE: break; default: // the other tags are for internal use @@ -3263,13 +3648,13 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, // FIXME XHTML // Other such tags? What about the other text ranges? - vector::const_iterator cit = tagsToClose.begin(); - vector::const_iterator cen = tagsToClose.end(); + vector::const_iterator cit = tagsToClose.begin(); + vector::const_iterator cen = tagsToClose.end(); for (; cit != cen; ++cit) xs << *cit; - vector::const_iterator sit = tagsToOpen.begin(); - vector::const_iterator sen = tagsToOpen.end(); + vector::const_iterator sit = tagsToOpen.begin(); + vector::const_iterator sen = tagsToOpen.end(); for (; sit != sen; ++sit) xs << *sit; @@ -3288,8 +3673,12 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, retval += inset->xhtml(xs, np); } } else { - char_type c = getUChar(buf.masterBuffer()->params(), i); - xs << c; + char_type c = getUChar(buf.masterBuffer()->params(), + runparams, i); + if (c == ' ' && (style.free_spacing || runparams.free_spacing)) + xs << XMLStream::ESCAPE_NONE << " "; + else + xs << c; } font_old = font.fontInfo(); } @@ -3341,10 +3730,12 @@ bool Paragraph::isLineSeparator(pos_type pos) const } -bool Paragraph::isWordSeparator(pos_type pos) const +bool Paragraph::isWordSeparator(pos_type pos, bool const ignore_deleted) const { if (pos == size()) return true; + if (ignore_deleted && isDeleted(pos)) + return false; if (Inset const * inset = getInset(pos)) return !inset->isLetter(); // if we have a hard hyphen (no en- or emdash) or apostrophe @@ -3378,10 +3769,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; @@ -3399,10 +3795,31 @@ 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)) - return getInset(i)->needsCProtection(); + pos_type size = pos_type(d->text_.size()); + for (pos_type i = 0; i < size; ++i) { + if (!isInset(i)) + continue; + Inset const * ins = getInset(i); + if (ins->needsCProtection(maintext, fragile)) + return true; + if (ins->getLayout().latextype() == InsetLayout::ENVIRONMENT) + // Environments need cprotection regardless the content + return true; + // Now check math environments + InsetMath const * im = getInset(i)->asInsetMath(); + if (!im || im->cell(0).empty()) + continue; + switch(im->cell(0)[0]->lyxCode()) { + case MATH_AMSARRAY_CODE: + case MATH_SUBSTACK_CODE: + case MATH_ENV_CODE: + case MATH_XYMATRIX_CODE: + // these need cprotection + return true; + default: + break; + } + } return false; } @@ -3511,7 +3928,7 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options, const Out os.put(c); else if (c == META_INSET && (options & AS_STR_INSETS)) { if (c == META_INSET && (options & AS_STR_PLAINTEXT)) { - LASSERT(runparams != 0, return docstring()); + LASSERT(runparams != nullptr, return docstring()); getInset(i)->plaintext(os, *runparams); } else { getInset(i)->toString(os); @@ -3633,32 +4050,43 @@ bool Paragraph::allowEmpty() const bool Paragraph::brokenBiblio() const { - // there is a problem if there is no bibitem at position 0 or - // if there is another bibitem in the paragraph. - return d->layout_->labeltype == LABEL_BIBLIO + // There is a problem if there is no bibitem at position 0 in + // paragraphs that need one, if there is another bibitem in the + // paragraph or if this paragraph is not supposed to have + // a bibitem inset at all. + return ((d->layout_->labeltype == LABEL_BIBLIO && (d->insetlist_.find(BIBITEM_CODE) != 0 - || d->insetlist_.find(BIBITEM_CODE, 1) > 0); + || d->insetlist_.find(BIBITEM_CODE, 1) > 0)) + || (d->layout_->labeltype != LABEL_BIBLIO + && d->insetlist_.find(BIBITEM_CODE) != -1)); } int Paragraph::fixBiblio(Buffer const & buffer) { - // FIXME: What about the case where paragraph is not BIBLIO - // but there is an InsetBibitem? // FIXME: when there was already an inset at 0, the return value is 1, // which does not tell whether another inset has been remove; the // cursor cannot be correctly updated. - if (d->layout_->labeltype != LABEL_BIBLIO) - return 0; - bool const track_changes = buffer.params().track_changes; int bibitem_pos = d->insetlist_.find(BIBITEM_CODE); - bool const hasbibitem0 = bibitem_pos == 0; + // The case where paragraph is not BIBLIO + if (d->layout_->labeltype != LABEL_BIBLIO) { + if (bibitem_pos == -1) + // No InsetBibitem => OK + return 0; + // There is an InsetBibitem: remove it! + d->insetlist_.release(bibitem_pos); + eraseChar(bibitem_pos, track_changes); + return (bibitem_pos == 0) ? -1 : -bibitem_pos; + } + + bool const hasbibitem0 = bibitem_pos == 0; if (hasbibitem0) { bibitem_pos = d->insetlist_.find(BIBITEM_CODE, 1); - // There was an InsetBibitem at pos 0, and no other one => OK + // There was an InsetBibitem at pos 0, + // and no other one => OK if (bibitem_pos == -1) return 0; // there is a bibitem at the 0 position, but since @@ -3673,7 +4101,7 @@ int Paragraph::fixBiblio(Buffer const & buffer) } // We need to create an inset at the beginning - Inset * inset = 0; + Inset * inset = nullptr; if (bibitem_pos > 0) { // there was one somewhere in the paragraph, let's move it inset = d->insetlist_.release(bibitem_pos); @@ -3687,6 +4115,8 @@ int Paragraph::fixBiblio(Buffer const & buffer) insertInset(0, inset, font, Change(track_changes ? Change::INSERTED : Change::UNCHANGED)); + // This is needed to get the counters right + buffer.updateBuffer(); return 1; } @@ -3745,14 +4175,14 @@ Inset * Paragraph::releaseInset(pos_type pos) Inset * Paragraph::getInset(pos_type pos) { return (pos < pos_type(d->text_.size()) && d->text_[pos] == META_INSET) - ? d->insetlist_.get(pos) : 0; + ? d->insetlist_.get(pos) : nullptr; } Inset const * Paragraph::getInset(pos_type pos) const { return (pos < pos_type(d->text_.size()) && d->text_[pos] == META_INSET) - ? d->insetlist_.get(pos) : 0; + ? d->insetlist_.get(pos) : nullptr; } @@ -3909,13 +4339,13 @@ void Paragraph::deregisterWords() void Paragraph::locateWord(pos_type & from, pos_type & to, - word_location const loc) const + word_location const loc, bool const ignore_deleted) const { switch (loc) { case WHOLE_WORD_STRICT: if (from == 0 || from == size() - || isWordSeparator(from) - || isWordSeparator(from - 1)) { + || isWordSeparator(from, ignore_deleted) + || isWordSeparator(from - 1, ignore_deleted)) { to = from; return; } @@ -3923,13 +4353,13 @@ void Paragraph::locateWord(pos_type & from, pos_type & to, case WHOLE_WORD: // If we are already at the beginning of a word, do nothing - if (!from || isWordSeparator(from - 1)) + if (!from || isWordSeparator(from - 1, ignore_deleted)) break; // fall through case PREVIOUS_WORD: // always move the cursor to the beginning of previous word - while (from && !isWordSeparator(from - 1)) + while (from && !isWordSeparator(from - 1, ignore_deleted)) --from; break; case NEXT_WORD: @@ -3940,7 +4370,7 @@ void Paragraph::locateWord(pos_type & from, pos_type & to, break; } to = from; - while (to < size() && !isWordSeparator(to)) + while (to < size() && !isWordSeparator(to, ignore_deleted)) ++to; } @@ -4019,7 +4449,7 @@ Language * Paragraph::Private::locateSpellRange( ++from; // don't check empty range if (from >= to) - return 0; + return nullptr; // get current language Language * lang = getSpellLanguage(from); pos_type last = from; @@ -4120,13 +4550,16 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, if (!d->layout_->spellcheck || !inInset().allowSpellCheck()) return result; - locateWord(from, to, WHOLE_WORD); + locateWord(from, to, WHOLE_WORD, true); if (from == to || from >= size()) return result; 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())