X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=d69fa55bb539459b0760adc24d19724a6da75ec3;hb=2b02b73f4a59bb73f9e819a960ea98c86ef8716f;hp=3c4919ca6864b96d07418eb840d8717726413844;hpb=afb442c76c53c37789680cb9814863e02dfc9663;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 3c4919ca68..d69fa55bb5 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -30,16 +30,16 @@ #include "Language.h" #include "LaTeXFeatures.h" #include "Layout.h" -#include "Length.h" #include "Font.h" #include "FontList.h" #include "LyXRC.h" #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" @@ -61,9 +61,12 @@ #include "support/ExceptionMessage.h" #include "support/gettext.h" #include "support/lassert.h" +#include "support/Length.h" #include "support/lstrings.h" #include "support/textutils.h" +#include "output_docbook.h" +#include #include #include #include @@ -82,14 +85,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 +267,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) @@ -348,7 +343,10 @@ public: Change & running_change, Layout const & style, pos_type & i, - unsigned int & column); + unsigned int & column, + bool const fontswitch_inset, + bool const closeLanguage, + bool const lang_switched_at_inset); /// void latexSpecialChar( @@ -445,10 +443,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; } @@ -458,7 +456,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; @@ -485,9 +483,6 @@ public: /// FontList fontlist_; - /// - int id_; - /// ParagraphParameters params_; @@ -512,11 +507,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); } @@ -529,17 +526,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()); } @@ -547,11 +543,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; @@ -581,18 +577,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); @@ -610,6 +594,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 { @@ -660,8 +666,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); } @@ -955,7 +961,10 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, Change & running_change, Layout const & style, pos_type & i, - unsigned int & column) + unsigned int & column, + bool const fontswitch_inset, + bool const closeLanguage, + bool const lang_switched_at_inset) { Inset * inset = owner_->getInset(i); LBUFERR(inset); @@ -1025,41 +1034,64 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, close = true; } - // FIXME: Bug: we can have an empty font change here! - // if there has just been a font change, we are going to close it - // right now, which means stupid latex code like \textsf{}. AFAIK, - // this does not harm dvi output. A minor bug, thus (JMarc) - - // Some insets cannot be inside a font change command. - // However, even such insets *can* be placed in \L or \R - // or their equivalents (for RTL language switches), so we don't - // close the language in those cases. - // ArabTeX, though, cannot handle this special behavior, it seems. - bool arabtex = basefont.language()->lang() == "arabic_arabtex" - || running_font.language()->lang() == "arabic_arabtex"; - if (open_font && !inset->inheritFont()) { - bool needPar = false; - bool closeLanguage = arabtex - || basefont.isRightToLeft() == running_font.isRightToLeft(); - unsigned int count = running_font.latexWriteEndChanges(os, - bparams, runparams, basefont, basefont, - needPar, closeLanguage); - column += count; - // if any font properties were closed, update the running_font, - // making sure, however, to leave the language as it was - if (count > 0) { - // FIXME: probably a better way to keep track of the old - // language, than copying the entire font? - Font const copy_font(running_font); - basefont = owner_->getLayoutFont(bparams, outerfont); - 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()); - if (closeLanguage) - runparams.local_font = &basefont; + if (open_font && fontswitch_inset) { + bool lang_closed = false; + // Close language if needed + if (closeLanguage && !lang_switched_at_inset) { + // We need prev_font here as language changes directly at inset + // will only be started inside the inset. + Font const prev_font = (i > 0) ? + owner_->getFont(bparams, i - 1, outerfont) + : running_font; + Font tmpfont(basefont); + tmpfont.setLanguage(prev_font.language()); + bool needPar = false; + unsigned int count = tmpfont.latexWriteEndChanges(os, bparams, runparams, + basefont, basefont, + needPar, closeLanguage); + column += count; + lang_closed = count > 0; } + // Update the running_font, making sure, however, + // to leave the language as it was. + // FIXME: probably a better way to keep track of the old + // language, than copying the entire font? + Font const copy_font(running_font); + basefont = owner_->getLayoutFont(bparams, outerfont); + running_font = basefont; + if (!closeLanguage) + running_font.setLanguage(copy_font.language()); + OutputParams rp = runparams; + rp.encoding = basefont.language()->encoding(); + // For these, we use switches, so they should be taken as + // base inside the inset. + basefont.fontInfo().setSize(copy_font.fontInfo().size()); + basefont.fontInfo().setFamily(copy_font.fontInfo().family()); + basefont.fontInfo().setSeries(copy_font.fontInfo().series()); + // Now re-do font changes in a way needed here + // (using switches with multi-par insets) + InsetText const * textinset = inset->asInsetText(); + bool const cprotect = textinset + ? textinset->hasCProtectContent(runparams.moving_arg) + && !textinset->text().isMainText() + : false; + unsigned int count2 = basefont.latexWriteStartChanges(os, bparams, + rp, running_font, + basefont, true, + cprotect); + open_font = true; + column += count2; + if (count2 == 0 && (lang_closed || lang_switched_at_inset)) + // All fonts closed + open_font = false; + if (closeLanguage) + runparams.local_font = &basefont; + } + + if (fontswitch_inset && !closeLanguage) { + // The directionality has been switched at inset. + // Force markup inside. + runparams.local_font = &basefont; } size_t const previous_row_count = os.texrow().rows(); @@ -1072,7 +1104,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, // add location information and throw again. e.par_id = id_; e.pos = i; - throw(e); + throw; } if (close) @@ -1754,7 +1786,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; @@ -1802,7 +1834,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); @@ -2043,13 +2075,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 { @@ -2112,15 +2137,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; @@ -2387,6 +2414,11 @@ void Paragraph::latex(BufferParams const & bparams, 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 ']' @@ -2396,9 +2428,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? @@ -2443,7 +2475,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, @@ -2477,9 +2509,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()) { @@ -2505,7 +2542,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); @@ -2525,7 +2563,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); @@ -2541,14 +2580,48 @@ void Paragraph::latex(BufferParams const & bparams, ++column; // Fully instantiated font - Font const current_font = getFont(bparams, i, outerfont); + Font current_font = getFont(bparams, i, outerfont); + // Previous font + Font const prev_font = (i > 0) ? + getFont(bparams, i - 1, outerfont) + : current_font; Font const last_font = running_font; + bool const in_ct_deletion = (bparams.output_changes + && runningChange == change + && change.type == Change::DELETED + && !os.afterParbreak()); + // Insets where font switches are used (rather than font commands) + bool const fontswitch_inset = + c == META_INSET + && getInset(i) + && getInset(i)->allowMultiPar() + && getInset(i)->lyxCode() != ERT_CODE; + + bool closeLanguage = false; + bool lang_switched_at_inset = false; + if (fontswitch_inset) { + // Some insets cannot be inside a font change command. + // However, even such insets *can* be placed in \L or \R + // or their equivalents (for RTL language switches), + // so we don't close the language in those cases + // (= differing isRightToLeft()). + // ArabTeX, though, doesn't seem to handle this special behavior. + closeLanguage = basefont.isRightToLeft() == current_font.isRightToLeft() + || basefont.language()->lang() == "arabic_arabtex" + || current_font.language()->lang() == "arabic_arabtex"; + // We need to check prev_font as language changes directly at inset + // will only be started inside the inset. + lang_switched_at_inset = prev_font.language() != current_font.language(); + } // Do we need to close the previous font? + bool langClosed = false; if (open_font && - (current_font != running_font || - current_font.language() != running_font.language())) + ((current_font != running_font + || current_font.language() != running_font.language()) + || (fontswitch_inset + && (current_font == prev_font)))) { // ensure there is no open script-wrapper if (!alien_script.empty()) { @@ -2557,12 +2630,32 @@ void Paragraph::latex(BufferParams const & bparams, 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; + } + if (closeLanguage) + // Force language closing + current_font.setLanguage(basefont.language()); + Font const nextfont = (i == body_pos-1) ? basefont : current_font; column += running_font.latexWriteEndChanges( os, bparams, runparams, basefont, - (i == body_pos-1) ? basefont : current_font, - needPar); - running_font = basefont; + nextfont, 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); + } open_font = false; + // Has the language been closed in the latexWriteEndChanges() call above? + langClosed = running_font.language() != basefont.language() + && running_font.language() != nextfont.language() + && (running_font.language()->encoding()->package() != Encoding::CJK); + running_font = basefont; } // if necessary, close language environment before opening CJK @@ -2579,7 +2672,8 @@ void Paragraph::latex(BufferParams const & bparams, } // Switch file encoding if necessary (and allowed) - if (!runparams.pass_thru && !style.pass_thru && + if ((!fontswitch_inset || closeLanguage) + && !runparams.pass_thru && !style.pass_thru && runparams.encoding->package() != Encoding::none && current_font.language()->encoding()->package() != Encoding::none) { pair const enc_switch = @@ -2591,8 +2685,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 @@ -2608,37 +2700,63 @@ 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, - runparams, basefont, - last_font); - // 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 - && runparams.inulemcmd) { - if (os.afterParbreak()) - os << "\\noindent"; - else - os << "\\\\\n"; - } - running_font = current_font; - open_font = true; - docstring fontchange = ods.str(); - // check whether the fontchange ends with a \\textcolor - // modifier and the text starts with a 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 + if (!fontswitch_inset) { + 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; + InsetText const * textinset = inInset().asInsetText(); + bool const cprotect = textinset + ? textinset->hasCProtectContent(runparams.moving_arg) + && !textinset->text().isMainText() + : false; + column += current_font.latexWriteStartChanges(ots, bparams, + runparams, basefont, last_font, false, + cprotect); + // 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 + && runparams.inulemcmd) { + if (os.afterParbreak()) + os << "\\noindent"; + else + os << "\\\\\n"; + } + running_font = current_font; + open_font = true; + docstring fontchange = ots.str(); os << fontchange; + // check whether the fontchange ends with a \\textcolor + // 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 << 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); + } + } else { + running_font = current_font; + open_font = !langClosed; + } } // FIXME: think about end_pos implementation... @@ -2680,7 +2798,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 = @@ -2690,21 +2808,47 @@ 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; incremented = true; } } + // We need to restore parts of this after insets with + // allowMultiPar() true + Font const save_basefont = basefont; d->latexInset(bparams, os, rp, running_font, - basefont, outerfont, open_font, - runningChange, style, i, column); + basefont, real_outerfont, open_font, + runningChange, style, i, column, fontswitch_inset, + closeLanguage, lang_switched_at_inset); + if (fontswitch_inset) { + if (open_font) { + bool needPar = false; + column += running_font.latexWriteEndChanges( + os, bparams, runparams, + basefont, basefont, needPar); + open_font = false; + } + basefont.fontInfo().setSize(save_basefont.fontInfo().size()); + basefont.fontInfo().setFamily(save_basefont.fontInfo().family()); + basefont.fontInfo().setSeries(save_basefont.fontInfo().series()); + } 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) @@ -2735,7 +2879,7 @@ void Paragraph::latex(BufferParams const & bparams, // add location information and throw again. e.par_id = id(); e.pos = i; - throw(e); + throw; } } } @@ -2749,6 +2893,10 @@ void Paragraph::latex(BufferParams const & bparams, // 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; } @@ -2772,8 +2920,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(); @@ -2868,7 +3016,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) { @@ -2877,7 +3025,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)) + "'"; } } } @@ -2885,25 +3033,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; @@ -2938,78 +3085,404 @@ 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 + + +std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, + OutputParams const & runparams, + Font const & outerfont, + pos_type initial, + bool is_last_par, + bool ignore_fonts) const +{ + // Track whether we have opened these tags + DocBookFontState fs; 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; - // parsing main loop + vector tagsToOpen; + vector tagsToClose; + + std::vector generatedParagraphs; + DocBookFontState old_fs = fs; + odocstringstream os; + auto * xs = new XMLStream(os); // XMLStream has no copy constructor: to create a new object, the only solution + // is to hold a pointer to the XMLStream (xs = XMLStream(os) is not allowed once the first object is built). + + // When a font tag ends with a space, output it after the closing font tag. This requires to store delayed + // characters at some point. + std::vector delayedChars; + + // 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; + // Don't show deleted material in the output. + if (isDeleted(i)) + continue; + + // If this is an InsetNewline, generate a new paragraph. Also reset the fonts, so that tags are closed in + // this paragraph. + if (getInset(i) != nullptr && getInset(i)->lyxCode() == NEWLINE_CODE) { + if (!ignore_fonts) + xs->closeFontTags(); + + // Output one paragraph (i.e. one string entry in generatedParagraphs). + generatedParagraphs.push_back(os.str()); + + // Create a new XMLStream for the new paragraph, completely independent from the previous one. This implies + // that the string stream must be reset. + os.str(from_ascii("")); + delete xs; + xs = new XMLStream(os); + + // Restore the fonts for the new paragraph, so that the right tags are opened for the new entry. + if (!ignore_fonts) { + font_old = outerfont.fontInfo(); + fs = old_fs; + } + } + + // Determine which tags should be opened or closed regarding fonts. + Font const font = getFont(buf.masterBuffer()->params(), i, outerfont); + if (!ignore_fonts) { + 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; + + // Deal with the delayed characters *after* closing font tags. + if (!delayedChars.empty()) { + for (char_type c: delayedChars) + *xs << c; + delayedChars.clear(); } + + 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); - } else { - char_type c = d->text_[i]; + if (!runparams.for_toc || inset->isInToc()) { + OutputParams np = runparams; + np.local_font = &font; - if (style.pass_thru) - os.put(c); + // TODO: special case will bite here. + np.docbook_in_par = true; + inset->docbook(*xs, np); + } + } else { + char_type c = getUChar(buf.masterBuffer()->params(), runparams, i); + if (lyx::isSpace(c) && !ignore_fonts) + delayedChars.push_back(c); else - os << sgml::escapeChar(c); + *xs << c; } font_old = font.fontInfo(); } - if (emph_flag) { - os << ""; - } + // FIXME, this code is just imported from XHTML + // I'm worried about what happens if a branch, say, is itself + // wrapped in some font stuff. I think that will not work. + if (!ignore_fonts) + xs->closeFontTags(); - 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, @@ -3048,8 +3521,8 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, 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) { @@ -3062,43 +3535,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(); @@ -3107,13 +3580,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: @@ -3127,15 +3600,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: @@ -3155,13 +3628,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; @@ -3177,19 +3650,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; @@ -3209,37 +3682,37 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, if (size_flag) { switch (old_size) { case TINY_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_TINY)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_TINY)); break; case SCRIPT_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_SCRIPT)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_SCRIPT)); break; case FOOTNOTE_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_FOOTNOTE)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_FOOTNOTE)); break; case SMALL_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_SMALL)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_SMALL)); break; case LARGE_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_LARGE)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_LARGE)); break; case LARGER_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_LARGER)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_LARGER)); break; case LARGEST_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_LARGEST)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_LARGEST)); break; case HUGE_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_HUGE)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_HUGE)); break; case HUGER_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_HUGER)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_HUGER)); break; case INCREASE_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_INCREASE)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_INCREASE)); break; case DECREASE_SIZE: - tagsToClose.push_back(html::EndFontTag(html::FT_SIZE_DECREASE)); + tagsToClose.emplace_back(xhtmlEndFontTag(xml::FT_SIZE_DECREASE)); break; case INHERIT_SIZE: case NORMAL_SIZE: @@ -3253,51 +3726,51 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, } switch (curr_size) { case TINY_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_TINY)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_TINY)); size_flag = true; break; case SCRIPT_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_SCRIPT)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_SCRIPT)); size_flag = true; break; case FOOTNOTE_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_FOOTNOTE)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_FOOTNOTE)); size_flag = true; break; case SMALL_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_SMALL)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_SMALL)); size_flag = true; break; case LARGE_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_LARGE)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_LARGE)); size_flag = true; break; case LARGER_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_LARGER)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_LARGER)); size_flag = true; break; case LARGEST_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_LARGEST)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_LARGEST)); size_flag = true; break; case HUGE_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_HUGE)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_HUGE)); size_flag = true; break; case HUGER_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_HUGER)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_HUGER)); size_flag = true; break; case INCREASE_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_INCREASE)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_INCREASE)); size_flag = true; break; case DECREASE_SIZE: - tagsToOpen.push_back(html::FontTag(html::FT_SIZE_DECREASE)); + tagsToOpen.emplace_back(xhtmlStartFontTag(xml::FT_SIZE_DECREASE)); size_flag = true; break; - case NORMAL_SIZE: case INHERIT_SIZE: + case NORMAL_SIZE: break; default: // the other tags are for internal use @@ -3309,13 +3782,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; @@ -3337,7 +3810,7 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, char_type c = getUChar(buf.masterBuffer()->params(), runparams, i); if (c == ' ' && (style.free_spacing || runparams.free_spacing)) - xs << XHTMLStream::ESCAPE_NONE << " "; + xs << XMLStream::ESCAPE_NONE << " "; else xs << c; } @@ -3391,10 +3864,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 @@ -3419,8 +3894,8 @@ bool Paragraph::isHardHyphenOrApostrophe(pos_type pos) const char_type const c = d->text_[pos]; if (c != '-' && c != '\'') return false; - int nextpos = pos + 1; - int prevpos = pos > 0 ? pos - 1 : 0; + pos_type nextpos = pos + 1; + pos_type prevpos = pos > 0 ? pos - 1 : 0; if ((nextpos == psize || isSpace(nextpos)) && (pos == 0 || isSpace(prevpos))) return false; @@ -3461,9 +3936,6 @@ bool Paragraph::needsCProtection(bool const fragile) const 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()) @@ -3587,7 +4059,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); @@ -3724,7 +4196,7 @@ bool Paragraph::brokenBiblio() const int Paragraph::fixBiblio(Buffer const & buffer) { // 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 + // which does not tell whether another inset has been removed; the // cursor cannot be correctly updated. bool const track_changes = buffer.params().track_changes; @@ -3756,6 +4228,8 @@ int Paragraph::fixBiblio(Buffer const & buffer) // than keep the first? (JMarc) Inset * inset = releaseInset(bibitem_pos); d->insetlist_.begin()->inset = inset; + // This needs to be done to update the counter (#8499) + buffer.updateBuffer(); return -bibitem_pos; } @@ -3834,14 +4308,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; } @@ -3895,10 +4369,8 @@ void Paragraph::changeCase(BufferParams const & bparams, pos_type pos, } int erasePos = pos - changes.size(); - for (size_t i = 0; i < changes.size(); i++) { - insertChar(pos, changes[i].first, - changes[i].second, - trackChanges); + for (auto const & change : changes) { + insertChar(pos, change.first, change.second, trackChanges); if (!eraseChar(erasePos, trackChanges)) { ++erasePos; ++pos; // advance @@ -3921,9 +4393,12 @@ int Paragraph::find(docstring const & str, bool cs, bool mw, // Ignore "invisible" letters such as ligature breaks // and hyphenation chars while searching while (pos < parsize - 1 && isInset(pos)) { + Inset const * inset = getInset(pos); + if (!inset->isLetter()) + break; odocstringstream os; - getInset(pos)->toString(os); - if (!getInset(pos)->isLetter() || !os.str().empty()) + inset->toString(os); + if (!os.str().empty()) break; pos++; } @@ -3998,13 +4473,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; } @@ -4012,13 +4487,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: @@ -4029,7 +4504,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; } @@ -4108,7 +4583,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; @@ -4117,7 +4592,18 @@ Language * Paragraph::Private::locateSpellRange( while (last < to && samelang && sameinset) { // hop to end of word while (last < to && !owner_->isWordSeparator(last)) { - if (owner_->getInset(last)) { + Inset const * inset = owner_->getInset(last); + if (inset && dynamic_cast(inset)) { + // check for "invisible" letters such as ligature breaks + odocstringstream os; + inset->toString(os); + if (os.str().length() != 0) { + // avoid spell check of visible special char insets + // stop the loop in front of the special char inset + sameinset = false; + break; + } + } else if (inset) { appendSkipPosition(skips, last); } else if (owner_->isDeleted(last)) { appendSkipPosition(skips, last); @@ -4209,7 +4695,7 @@ 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; @@ -4343,7 +4829,7 @@ void Paragraph::spellCheck() const // start the spell checker on the unit of meaning docstring word = asString(first, last, AS_STR_INSETS + AS_STR_SKIPDELETE); WordLangTuple wl = WordLangTuple(word, lang); - SpellChecker::Result result = word.size() ? + SpellChecker::Result result = !word.empty() ? speller->check(wl) : SpellChecker::WORD_OK; d->markMisspelledWords(first, last, result, word, skips); first = ++last;