#include "insets/InsetBibitem.h"
#include "insets/InsetLabel.h"
#include "insets/InsetSpecialChar.h"
+#include "insets/InsetText.h"
+
+#include "mathed/InsetMathHull.h"
#include "support/debug.h"
#include "support/docstring_list.h"
using namespace std;
using namespace lyx::support;
+// OSX clang, gcc < 4.8.0, and msvc < 2015 do not support C++11 thread_local
+#if defined(__APPLE__) || (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 8)
+#define THREAD_LOCAL_STATIC static __thread
+#elif defined(_MSC_VER) && (_MSC_VER < 1900)
+#define THREAD_LOCAL_STATIC static __declspec(thread)
+#else
+#define THREAD_LOCAL_STATIC thread_local static
+#endif
+
namespace lyx {
namespace {
/// Inset identifier (above 0x10ffff, for ucs-4)
char_type const META_INSET = 0x200001;
-}
+} // namespace
/////////////////////////////////////////////////////////////////////
pos_type i,
unsigned int & column);
///
+ bool latexSpecialTU(
+ char_type const c,
+ otexstream & os,
+ pos_type i,
+ unsigned int & column);
+ ///
bool latexSpecialT3(
char_type const c,
otexstream & os,
typedef SkipPositions::const_iterator SkipPositionsIterator;
void appendSkipPosition(SkipPositions & skips, pos_type const pos) const;
-
+
Language * getSpellLanguage(pos_type const from) const;
Language * locateSpellRange(pos_type & from, pos_type & to,
}
bool ignoreWord(docstring const & word) const ;
-
+
void setMisspelled(pos_type from, pos_type to, SpellChecker::Result state)
{
pos_type textsize = owner_->size();
}
-void Paragraph::addChangesToToc(DocIterator const & cdit,
- Buffer const & buf, bool output_active) const
+void Paragraph::addChangesToToc(DocIterator const & cdit, Buffer const & buf,
+ bool output_active, TocBackend & backend) const
{
- d->changes_.addToToc(cdit, buf, output_active);
+ d->changes_.addToToc(cdit, buf, output_active, backend);
}
os << '\n';
} else {
if (open_font) {
+ bool needPar = false;
column += running_font.latexWriteEndChanges(
os, bparams, runparams,
- basefont, basefont);
+ basefont, basefont, needPar);
open_font = false;
}
// decorations at all
&& inset->lyxCode() != ERT_CODE) {
if (running_font.language()->lang() == "farsi")
- os << "\\beginL{}";
+ os << "\\beginL" << termcmd;
else
os << "\\L{";
close = true;
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, closeLanguage);
+ 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
}
}
- int prev_rows = os.texrow().rows();
+ size_t const previous_row_count = os.texrow().rows();
try {
runparams.lastid = id_;
if (close) {
if (running_font.language()->lang() == "farsi")
- os << "\\endL{}";
+ os << "\\endL" << termcmd;
else
os << '}';
}
- if (os.texrow().rows() > prev_rows) {
+ if (os.texrow().rows() > previous_row_count) {
os.texrow().start(owner_->id(), i + 1);
column = 0;
} else {
// non-standard font encoding. If we are using such a language,
// we do not output special T1 chars.
if (!runparams.inIPA && !running_font.language()->internalFontEncoding()
- && bparams.font_encoding() == "T1" && latexSpecialT1(c, os, i, column))
+ && !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
+ else if (!runparams.inIPA && !running_font.language()->internalFontEncoding()
+ && runparams.isFullUnicode() && latexSpecialTU(c, os, i, column))
+ return;
// Otherwise, we use what LaTeX provides us.
switch (c) {
case '\\':
- os << "\\textbackslash{}";
+ os << "\\textbackslash" << termcmd;
column += 15;
break;
case '<':
- os << "\\textless{}";
+ os << "\\textless" << termcmd;
column += 10;
break;
case '>':
- os << "\\textgreater{}";
+ os << "\\textgreater" << termcmd;
column += 13;
break;
case '|':
- os << "\\textbar{}";
+ os << "\\textbar" << termcmd;
column += 9;
break;
case '-':
}
break;
case '\"':
- os << "\\char`\\\"{}";
- column += 9;
+ os << "\\textquotedbl" << termcmd;
+ column += 14;
break;
case '$': case '&':
break;
case '~':
- os << "\\textasciitilde{}";
+ os << "\\textasciitilde" << termcmd;
column += 16;
break;
case '^':
- os << "\\textasciicircum{}";
+ os << "\\textasciicircum" << termcmd;
column += 17;
break;
// written. (Asger)
break;
+ case 0x2013:
+ case 0x2014:
+ // XeTeX's dash behaviour is determined via a global setting
+ if (bparams.use_dash_ligatures
+ && owner_->getFontSettings(bparams, i).fontInfo().family() != TYPEWRITER_FAMILY
+ && (!bparams.useNonTeXFonts || runparams.flavor != OutputParams::XETEX)) {
+ if (c == 0x2013) {
+ // en-dash
+ os << "--";
+ column +=2;
+ } else {
+ // em-dash
+ os << "---";
+ column +=3;
+ }
+ break;
+ }
+ // fall through
default:
if (c == '\0')
return;
// but we should avoid ligatures
if (i + 1 >= int(text_.size()) || text_[i + 1] != c)
return true;
- os << "\\textcompwordmark{}";
+ os << "\\textcompwordmark" << termcmd;
column += 19;
return true;
case '|':
return true;
case '\"':
// soul.sty breaks with \char`\"
- os << "\\textquotedbl{}";
+ os << "\\textquotedbl" << termcmd;
column += 14;
return true;
default:
}
+bool Paragraph::Private::latexSpecialTU(char_type const c, otexstream & os,
+ pos_type i, unsigned int & column)
+{
+ // TU encoding is currently on par with T1.
+ return latexSpecialT1(c, os, i, column);
+}
+
+
bool Paragraph::Private::latexSpecialT3(char_type const c, otexstream & os,
pos_type /*i*/, unsigned int & column)
{
os.put(c);
return true;
case '|':
- os << "\\textvertline{}";
+ os << "\\textvertline" << termcmd;
column += 14;
return true;
+ case 0x2013:
+ os << "\\textendash" << termcmd;
+ column += 12;
+ return true;
+ case 0x2014:
+ os << "\\textemdash" << termcmd;
+ column += 12;
+ return true;
default:
return false;
}
void Paragraph::Private::validate(LaTeXFeatures & features) const
{
+ Buffer const & buf = inset_owner_->buffer();
+ BufferParams const & bp = features.runparams().is_child
+ ? buf.masterParams() : buf.params();
if (layout_->inpreamble && inset_owner_) {
bool const is_command = layout_->latextype == LATEX_COMMAND;
- Buffer const & buf = inset_owner_->buffer();
- BufferParams const & bp = features.runparams().is_child
- ? buf.masterParams() : buf.params();
Font f;
// Using a string stream here circumvents the encoding
// switching machinery of odocstream. Therefore the
// output is wrong if this paragraph contains content
// that needs to switch encoding.
- odocstringstream ods;
- otexstream os(ods);
+ otexstringstream os;
+ os << layout_->preamble();
if (is_command) {
os << '\\' << from_ascii(layout_->latexname());
// we have to provide all the optional arguments here, even though
}
os << from_ascii(layout_->latexparam());
}
- docstring::size_type const length = ods.str().length();
+ size_t const length = os.length();
// this will output "{" at the beginning, but not at the end
owner_->latex(bp, f, os, features.runparams(), 0, -1, true);
- if (ods.str().length() > length) {
+ if (os.length() > length) {
if (is_command) {
- ods << '}';
+ os << '}';
if (!layout_->postcommandargs().empty()) {
OutputParams rp = features.runparams();
rp.local_font = &owner_->getFirstFontSettings(bp);
latexArgInsets(*owner_, os, rp, layout_->postcommandargs(), "post:");
}
}
- string const snippet = to_utf8(ods.str());
- features.addPreambleSnippet(snippet, true);
+ features.addPreambleSnippet(os.release(), true);
}
}
InsetList::const_iterator iend = insetlist_.end();
for (; icit != iend; ++icit) {
if (icit->inset) {
+ features.inDeletedInset(owner_->isDeleted(icit->pos));
+ if (icit->inset->lyxCode() == FOOT_CODE) {
+ // FIXME: an item inset would make things much easier.
+ if ((layout_->latextype == LATEX_LIST_ENVIRONMENT
+ || (layout_->latextype == LATEX_ITEM_ENVIRONMENT
+ && layout_->margintype == MARGIN_FIRST_DYNAMIC))
+ && (icit->pos < begin_of_body_
+ || (icit->pos == begin_of_body_
+ && (icit->pos == 0 || text_[icit->pos - 1] != ' '))))
+ features.saveNoteEnv("description");
+ }
icit->inset->validate(features);
+ features.inDeletedInset(false);
if (layout_->needprotect &&
icit->inset->lyxCode() == FOOT_CODE)
features.require("NeedLyXFootnoteCode");
// then the contents
for (pos_type i = 0; i < int(text_.size()) ; ++i) {
- BufferEncodings::validate(text_[i], features);
+ char_type c = text_[i];
+ 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)
+ // XeTeX's dash behaviour is determined via a global setting
+ features.require("xetexdashbreakstate");
+ BufferEncodings::validate(c, features);
}
}
s.erase();
}
-}
+} // namespace
void Paragraph::write(ostream & os, BufferParams const & bparams,
}
-char Paragraph::getAlign() const
+LyXAlignment Paragraph::getAlign() const
{
if (d->params_.align() == LYX_ALIGN_LAYOUT)
return d->layout_->align;
return true;
}
-} // namespace anon
+} // namespace
int Paragraph::Private::startTeXParParams(BufferParams const & bparams,
column += d->startTeXParParams(bparams, os, runparams);
}
+ // Whether a \par can be issued for insets typeset inline with text.
+ // Yes if greater than 0. This has to be static.
+ THREAD_LOCAL_STATIC int parInline = 0;
+
for (pos_type i = 0; i < size(); ++i) {
// First char in paragraph or after label?
if (i == body_pos) {
if (body_pos > 0) {
if (open_font) {
+ bool needPar = false;
column += running_font.latexWriteEndChanges(
os, bparams, runparams,
- basefont, basefont);
+ basefont, basefont, needPar);
open_font = false;
}
basefont = getLayoutFont(bparams, outerfont);
runparams);
}
+ runparams.wasDisplayMath = runparams.inDisplayMath;
+ runparams.inDisplayMath = false;
+ bool deleted_display_math = false;
Change const & change = runparams.inDeletedInset
? runparams.changeOfDeletedInset : lookupChange(i);
+ // Check whether a display math inset follows
+ if (d->text_[i] == META_INSET
+ && i >= start_pos && (end_pos == -1 || i < end_pos)) {
+ InsetMath const * im = getInset(i)->asInsetMath();
+ if (im && im->asHullInset()
+ && im->asHullInset()->outerDisplay()) {
+ runparams.inDisplayMath = true;
+ // runparams.inDeletedInset will be set by
+ // latexInset later, but we need this info
+ // before it is called. On the other hand, we
+ // cannot set it here because it is a counter.
+ deleted_display_math = isDeleted(i);
+ }
+ if (bparams.output_changes && deleted_display_math
+ && runningChange == change
+ && change.type == Change::DELETED
+ && !os.afterParbreak()) {
+ // A display math in the same paragraph follows.
+ // We have to close and then reopen \lyxdeleted,
+ // otherwise the math will be shifted up.
+ OutputParams rp = runparams;
+ if (open_font) {
+ bool needPar = false;
+ column += running_font.latexWriteEndChanges(
+ os, bparams, rp, basefont,
+ basefont, needPar);
+ open_font = false;
+ }
+ basefont = getLayoutFont(bparams, outerfont);
+ running_font = basefont;
+ column += Changes::latexMarkChange(os, bparams,
+ Change(Change::INSERTED), change, rp);
+ }
+ }
+
if (bparams.output_changes && runningChange != change) {
if (open_font) {
+ bool needPar = false;
column += running_font.latexWriteEndChanges(
- os, bparams, runparams, basefont, basefont);
+ os, bparams, runparams,
+ basefont, basefont, needPar);
open_font = false;
}
basefont = getLayoutFont(bparams, outerfont);
running_font = basefont;
-
column += Changes::latexMarkChange(os, bparams, runningChange,
change, runparams);
runningChange = change;
(current_font != running_font ||
current_font.language() != running_font.language()))
{
+ bool needPar = false;
column += running_font.latexWriteEndChanges(
- os, bparams, runparams, basefont,
- (i == body_pos-1) ? basefont : current_font);
+ os, bparams, runparams, basefont,
+ (i == body_pos-1) ? basefont : current_font,
+ needPar);
running_font = basefont;
open_font = false;
}
// 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 (runparams.use_polyglossia)
- popPolyglossiaLang();
+ if (using_begin_end)
+ popLanguageName();
}
// Switch file encoding if necessary (and allowed)
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
+ // line to accommodate such box. This has to be done before
+ // writing any font changing commands.
+ if (runparams.inDisplayMath && !deleted_display_math
+ && runparams.inulemcmd) {
+ if (os.afterParbreak())
+ os << "\\noindent";
+ else
+ os << "\\\\\n";
+ }
+
// Do we need to change font?
if ((current_font != running_font ||
current_font.language() != running_font.language()) &&
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();
// and then split to handle the two modes separately.
if (c == META_INSET) {
if (i >= start_pos && (end_pos == -1 || i < end_pos)) {
+ // Greyedout notes and, in general, all insets
+ // with InsetLayout::isDisplay() == false,
+ // are typeset inline with the text. So, we
+ // can add a \par to the last paragraph of
+ // such insets only if nothing else follows.
+ bool incremented = false;
+ Inset const * inset = getInset(i);
+ InsetText const * textinset = inset
+ ? inset->asInsetText()
+ : 0;
+ if (i + 1 == size() && textinset
+ && !inset->getLayout().isDisplay()) {
+ ParagraphList const & pars =
+ textinset->text().paragraphs();
+ pit_type const pit = pars.size() - 1;
+ Font const last_font =
+ pit < 0 || pars[pit].empty()
+ ? pars[pit].getLayoutFont(
+ bparams,
+ outerfont)
+ : pars[pit].getFont(bparams,
+ pars[pit].size() - 1,
+ outerfont);
+ if (last_font.fontInfo().size() !=
+ basefont.fontInfo().size()) {
+ ++parInline;
+ incremented = true;
+ }
+ }
d->latexInset(bparams, os, rp, running_font,
basefont, outerfont, open_font,
runningChange, style, i, column);
+ if (incremented)
+ --parInline;
}
- } else {
- if (i >= start_pos && (end_pos == -1 || i < end_pos)) {
- try {
- d->latexSpecialChar(os, bparams, rp, running_font, runningChange,
- style, i, end_pos, column);
- } catch (EncodingException & e) {
+ } else if (i >= start_pos && (end_pos == -1 || i < end_pos)) {
+ try {
+ d->latexSpecialChar(os, bparams, rp,
+ running_font, runningChange,
+ style, i, end_pos, column);
+ } catch (EncodingException & e) {
if (runparams.dryrun) {
os << "<" << _("LyX Warning: ")
<< _("uncodable character") << " '";
}
}
}
- }
// Set the encoding to that returned from latexSpecialChar (see
// comment for encoding member in OutputParams.h)
runparams.encoding = rp.encoding;
+
+ // Also carry on the info on a closed ulem command for insets
+ // 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;
}
// If we have an open font definition, we have to close it
if (open_font) {
+ // Make sure that \\par is done with the font of the last
+ // character if this has another size as the default.
+ // This is necessary because LaTeX (and LyX on the screen)
+ // calculates the space between the baselines according
+ // to this font. (Matthias)
+ //
+ // We must not change the font for the last paragraph
+ // of non-multipar insets, tabular cells or commands,
+ // since this produces unwanted whitespace.
+
+ Font const font = empty()
+ ? getLayoutFont(bparams, outerfont)
+ : getFont(bparams, size() - 1, outerfont);
+
+ InsetText const * textinset = inInset().asInsetText();
+
+ bool const maintext = textinset
+ ? textinset->text().isMainText()
+ : false;
+
+ size_t const numpars = textinset
+ ? textinset->text().paragraphs().size()
+ : 0;
+
+ bool needPar = false;
+
+ if (style.resfont.size() != font.fontInfo().size()
+ && (!runparams.isLastPar || maintext
+ || (numpars > 1 && d->ownerCode() != CELL_CODE
+ && (inInset().getLayout().isDisplay()
+ || parInline)))
+ && !style.isCommand()) {
+ needPar = true;
+ }
#ifdef FIXED_LANGUAGE_END_DETECTION
if (next_) {
running_font.latexWriteEndChanges(os, bparams,
runparams, basefont,
- next_->getFont(bparams, 0, outerfont));
+ next_->getFont(bparams, 0, outerfont),
+ needPar);
} else {
running_font.latexWriteEndChanges(os, bparams,
- runparams, basefont, basefont);
+ runparams, basefont, basefont, needPar);
}
#else
//FIXME: For now we ALWAYS have to close the foreign font settings if they are
//FIXME: there as we start another \selectlanguage with the next paragraph if
//FIXME: we are in need of this. This should be fixed sometime (Jug)
running_font.latexWriteEndChanges(os, bparams, runparams,
- basefont, basefont);
+ basefont, basefont, needPar);
#endif
+ if (needPar) {
+ // The \par could not be inserted at the same nesting
+ // level of the font size change, so do it now.
+ os << "{\\" << font.latexSize() << "\\par}";
+ }
}
column += Changes::latexMarkChange(os, bparams, runningChange,
flag = false;
}
}
-}
+} // namespace
docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
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;
FontShape curr_fs = INHERIT_SHAPE;
FontFamily curr_fam = INHERIT_FAMILY;
FontSize curr_size = FONT_SIZE_INHERIT;
-
- string const default_family =
- buf.masterBuffer()->params().fonts_default_family;
+
+ string const default_family =
+ buf.masterBuffer()->params().fonts_default_family;
vector<html::FontTag> tagsToOpen;
vector<html::EndFontTag> tagsToClose;
-
+
// parsing main loop
for (pos_type i = initial; i < size(); ++i) {
// let's not show deleted material in the output
curstate = font.fontInfo().underbar();
if (font_old.underbar() != curstate)
doFontSwitch(tagsToOpen, tagsToClose, ubar_flag, curstate, html::FT_UBAR);
-
+
// strikeout
curstate = font.fontInfo().strikeout();
if (font_old.strikeout() != curstate)
doFontSwitch(tagsToOpen, tagsToClose, sout_flag, curstate, html::FT_SOUT);
+ // xout
+ curstate = font.fontInfo().xout();
+ if (font_old.xout() != curstate)
+ doFontSwitch(tagsToOpen, tagsToClose, xout_flag, curstate, html::FT_XOUT);
+
// double underbar
curstate = font.fontInfo().uuline();
if (font_old.uuline() != curstate)
bool Paragraph::isNewline(pos_type pos) const
{
+ // U+2028 LINE SEPARATOR
+ // U+2029 PARAGRAPH SEPARATOR
+ char_type const c = d->text_[pos];
+ if (c == 0x2028 || c == 0x2029)
+ return true;
Inset const * inset = getInset(pos);
return inset && inset->lyxCode() == NEWLINE_CODE;
}
void Paragraph::forOutliner(docstring & os, size_t const maxlen,
- bool const shorten) const
+ bool const shorten, bool const label) const
{
size_t tmplen = shorten ? maxlen + 1 : maxlen;
- if (!d->params_.labelString().empty())
- os += d->params_.labelString() + ' ';
+ if (label && !labelString().empty())
+ os += labelString() + ' ';
for (pos_type i = 0; i < size() && os.length() < tmplen; ++i) {
if (isDeleted(i))
continue;
InsetCommandParams(BIBITEM_CODE));
Font font(inherit_font, buffer.params().language);
- insertInset(0, inset, font, Change(track_changes ? Change::INSERTED
+ insertInset(0, inset, font, Change(track_changes ? Change::INSERTED
: Change::UNCHANGED));
return 1;
}
-void Paragraph::setBuffer(Buffer & b)
+void Paragraph::setInsetBuffers(Buffer & b)
{
d->insetlist_.setBuffer(b);
}
+void Paragraph::resetBuffer()
+{
+ d->insetlist_.resetBuffer();
+}
+
+
Inset * Paragraph::releaseInset(pos_type pos)
{
Inset * inset = d->insetlist_.release(pos);
Private::LangWordsMap::const_iterator itl = d->words_.begin();
Private::LangWordsMap::const_iterator ite = d->words_.end();
for (; itl != ite; ++itl) {
- WordList * wl = theWordList(itl->first);
+ WordList & wl = theWordList(itl->first);
Private::Words::const_iterator it = (itl->second).begin();
Private::Words::const_iterator et = (itl->second).end();
for (; it != et; ++it)
- wl->remove(*it);
+ wl.remove(*it);
}
d->words_.clear();
}
to = from;
return;
}
- // no break here, we go to the next
+ // fall through
case WHOLE_WORD:
// If we are already at the beginning of a word, do nothing
if (!from || isWordSeparator(from - 1))
break;
- // no break here, we go to the next
+ // fall through
case PREVIOUS_WORD:
// always move the cursor to the beginning of previous word
Private::LangWordsMap::const_iterator itl = d->words_.begin();
Private::LangWordsMap::const_iterator ite = d->words_.end();
for (; itl != ite; ++itl) {
- WordList * wl = theWordList(itl->first);
+ WordList & wl = theWordList(itl->first);
Private::Words::const_iterator it = (itl->second).begin();
Private::Words::const_iterator et = (itl->second).end();
for (; it != et; ++it)
- wl->insert(*it);
+ wl.insert(*it);
}
}