Strike out (in the output) deleted display math with track-changes

Showing deleted display math by enabling "Show Changes in Output" was
only possible with dvi (through dvipost). Although LyX strikes out
such formulas on screen, it was impossible obtaining an output
directly using pdflatex (or other engines producing pdf) because
ulem cannot cope with display math material and gives errors.
The solution is to strike out by ourselves such deleted formulas.
I took into account several options. One of them would produce
an output similar to dvipost (which strikes out each element), but
would have required much more changes in the output routines.
Eventually, I opted for using tikz, which gives a more clean
output (as it requires to simply adding a preamble and a postamble
to the latex code of any displayed math, instead of a mark up
tailored to each particular math construct). The look of the pdf
output is similar to the way LyX strikes out the equations on screen.

Fixes #9678
This commit is contained in:
Enrico Forestieri 2016-11-05 21:31:09 +01:00
parent 3191178555
commit 8a1f936ff1
13 changed files with 220 additions and 17 deletions

View File

@ -1932,6 +1932,10 @@ void Buffer::writeLaTeXSource(otexstream & os,
// make the body.
os << "\\begin{document}\n";
// mark the start of a new paragraph by simulating a newline,
// so that os.afterParbreak() returns true at document start
os.lastChar('\n');
// output the parent macros
MacroSet::iterator it = parentMacros.begin();
MacroSet::iterator end = parentMacros.end();

View File

@ -421,7 +421,7 @@ int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
// close \lyxadded or \lyxdeleted
os << '}';
column++;
if (oldChange.type == Change::DELETED)
if (oldChange.type == Change::DELETED && !runparams.wasDisplayMath)
--runparams.inulemcmd;
}
@ -433,7 +433,8 @@ int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
docstring macro_beg;
if (change.type == Change::DELETED) {
macro_beg = from_ascii("\\lyxdeleted{");
++runparams.inulemcmd;
if (!runparams.inDisplayMath)
++runparams.inulemcmd;
}
else if (change.type == Change::INSERTED)
macro_beg = from_ascii("\\lyxadded{");
@ -442,6 +443,17 @@ int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
bparams.authors().get(change.author).name(),
chgTime, runparams);
// signature needed by \lyxsout to correctly strike out display math
if (change.type == Change::DELETED && runparams.inDisplayMath
&& (!LaTeXFeatures::isAvailable("dvipost")
|| (runparams.flavor != OutputParams::LATEX
&& runparams.flavor != OutputParams::DVILUATEX))) {
if (os.afterParbreak())
str += from_ascii("\\\\\\noindent\n");
else
str += from_ascii("\\\\\\\\\n");
}
os << str;
column += str.size();

View File

@ -191,12 +191,25 @@ static docstring const changetracking_dvipost_def = from_ascii(
static docstring const changetracking_xcolor_ulem_def = from_ascii(
"%% Change tracking with ulem\n"
"\\DeclareRobustCommand{\\lyxadded}[3]{{\\color{lyxadded}{}#3}}\n"
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\color{lyxdeleted}\\sout{#3}}}\n");
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\color{lyxdeleted}\\lyxsout{#3}}}\n"
"\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n");
static docstring const changetracking_xcolor_ulem_hyperref_def = from_ascii(
"%% Change tracking with ulem\n"
"\\DeclareRobustCommand{\\lyxadded}[3]{{\\texorpdfstring{\\color{lyxadded}{}}{}#3}}\n"
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\texorpdfstring{\\color{lyxdeleted}\\sout{#3}}{}}}\n");
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\texorpdfstring{\\color{lyxdeleted}\\lyxsout{#3}}{}}}\n"
"\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n");
static docstring const changetracking_tikz_math_sout_def = from_ascii(
"%% Strike out display math with tikz\n"
"\\usepackage{tikz}\n"
"\\usetikzlibrary{calc}\n"
"\\newcommand{\\lyxmathsout}[1]{%\n"
" \\tikz[baseline=(math.base)]{\n"
" \\node[inner sep=0pt,outer sep=0pt](math){#1};\n"
" \\draw($(math.south west)+(2em,.5em)$)--($(math.north east)-(2em,.5em)$);\n"
" }\n"
"}\n");
static docstring const changetracking_none_def = from_ascii(
"\\newcommand{\\lyxadded}[3]{#3}\n"
@ -388,7 +401,8 @@ static docstring const lyxstrikeout_style = from_ascii(
LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p,
OutputParams const & r)
: buffer_(&b), params_(p), runparams_(r), in_float_(false)
: buffer_(&b), params_(p), runparams_(r), in_float_(false),
in_deleted_inset_(false)
{}
@ -1367,6 +1381,9 @@ docstring const LaTeXFeatures::getMacros() const
macros << changetracking_xcolor_ulem_def;
}
if (mustProvide("ct-tikz-math-sout"))
macros << changetracking_tikz_math_sout_def;
if (mustProvide("ct-none"))
macros << changetracking_none_def;

View File

@ -156,6 +156,10 @@ public:
bool inFloat() const { return in_float_; }
/// are we in a float?
void inFloat(bool const b) { in_float_ = b; }
/// are we in a deleted inset?
bool inDeletedInset() const { return in_deleted_inset_; }
/// are we in a deleted inset?
void inDeletedInset(bool const b) { in_deleted_inset_ = b; }
/// Runparams that will be used for exporting this file.
OutputParams const & runparams() const { return runparams_; }
/// Resolve alternatives like "esint|amsmath|wasysym"
@ -209,6 +213,8 @@ private:
///
bool in_float_;
///
bool in_deleted_inset_;
///
docstring htmltitle_;
};

View File

@ -23,7 +23,7 @@ OutputParams::OutputParams(Encoding const * enc)
moving_arg(false), intitle(false), inulemcmd(0), local_font(0), master_language(0),
encoding(enc), free_spacing(false), use_babel(false), use_polyglossia(false),
use_indices(false), use_japanese(false), linelen(0), depth(0),
exportdata(new ExportData),
exportdata(new ExportData), inDisplayMath(false), wasDisplayMath(false),
inComment(false), inTableCell(NO), inFloat(NONFLOAT),
inIndexEntry(false), inIPA(false), inDeletedInset(0),
changeOfDeletedInset(Change::UNCHANGED),

View File

@ -184,6 +184,16 @@ public:
*/
shared_ptr<ExportData> exportdata;
/** Whether we are entering a display math inset.
* Needed to correctly strike out deleted math in change tracking.
*/
mutable bool inDisplayMath;
/** Whether we are leaving a display math inset.
* Needed to correctly track nested ulem commands in change tracking.
*/
mutable bool wasDisplayMath;
/** Whether we are inside a comment inset. Insets that are including
* external files like InsetGraphics, InsetInclude and InsetExternal
* may only write the usual output and must not attempt to do

View File

@ -52,6 +52,8 @@
#include "insets/InsetLabel.h"
#include "insets/InsetSpecialChar.h"
#include "mathed/InsetMathHull.h"
#include "support/debug.h"
#include "support/docstring_list.h"
#include "support/ExceptionMessage.h"
@ -1437,7 +1439,9 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const
InsetList::const_iterator iend = insetlist_.end();
for (; icit != iend; ++icit) {
if (icit->inset) {
features.inDeletedInset(owner_->isDeleted(icit->pos));
icit->inset->validate(features);
features.inDeletedInset(false);
if (layout_->needprotect &&
icit->inset->lyxCode() == FOOT_CODE)
features.require("NeedLyXFootnoteCode");
@ -2391,6 +2395,25 @@ void Paragraph::latex(BufferParams const & bparams,
runparams);
}
runparams.wasDisplayMath = runparams.inDisplayMath;
runparams.inDisplayMath = false;
bool deleted_display_math = false;
// 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);
}
}
Change const & change = runparams.inDeletedInset
? runparams.changeOfDeletedInset : lookupChange(i);
@ -2402,7 +2425,6 @@ void Paragraph::latex(BufferParams const & bparams,
}
basefont = getLayoutFont(bparams, outerfont);
running_font = basefont;
column += Changes::latexMarkChange(os, bparams, runningChange,
change, runparams);
runningChange = change;
@ -2464,6 +2486,19 @@ 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 \columnwidth, 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 ((font != running_font ||
font.language() != running_font.language()) &&
@ -2473,6 +2508,15 @@ void Paragraph::latex(BufferParams const & bparams,
column += 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 = font;
open_font = true;
docstring fontchange = ods.str();
@ -2524,12 +2568,12 @@ void Paragraph::latex(BufferParams const & bparams,
basefont, outerfont, open_font,
runningChange, style, i, column);
}
} 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") << " '";
@ -2543,11 +2587,15 @@ void Paragraph::latex(BufferParams const & bparams,
}
}
}
}
// 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

View File

@ -118,6 +118,37 @@ namespace {
}
// writes a preamble for underlined or struck out math display
void writeMathdisplayPreamble(WriteStream & os)
{
if (os.strikeoutMath()) {
if (os.ulemCmd() == WriteStream::UNDERLINE)
os << "\\raisebox{-\\belowdisplayshortskip}{"
"\\lyxmathsout{\\parbox[b]{\\columnwidth}{";
else
os << "\\lyxmathsout{\\parbox{\\columnwidth}{";
} else if (os.ulemCmd() == WriteStream::UNDERLINE)
os << "\\raisebox{-\\belowdisplayshortskip}{"
"\\parbox[b]{\\columnwidth}{";
else if (os.ulemCmd() == WriteStream::STRIKEOUT)
os << "\\parbox{\\columnwidth}{";
}
// writes a postamble for underlined or struck out math display
void writeMathdisplayPostamble(WriteStream & os)
{
if (os.strikeoutMath()) {
if (os.ulemCmd() == WriteStream::UNDERLINE)
os << "}";
os << "}}\\\\\n";
} else if (os.ulemCmd() == WriteStream::UNDERLINE)
os << "}}\\\\\n";
else if (os.ulemCmd() == WriteStream::STRIKEOUT)
os << "}\\\\\n";
}
} // end anon namespace
@ -892,6 +923,29 @@ bool InsetMathHull::ams() const
}
bool InsetMathHull::outerDisplay() const
{
switch (type_) {
case hullEquation:
case hullEqnArray:
case hullAlign:
case hullFlAlign:
case hullGather:
case hullMultline:
return true;
case hullNone:
case hullSimple:
case hullAlignAt:
case hullXAlignAt:
case hullXXAlignAt:
case hullUnknown:
case hullRegexp:
break;
}
return false;
}
Inset::DisplayType InsetMathHull::display() const
{
switch (type_) {
@ -957,6 +1011,9 @@ void InsetMathHull::validate(LaTeXFeatures & features) const
+ bgcol + string("}{\\ensuremath{\\mathtt{#1}}}}"));
features.addPreambleSnippet(
string("\\newcommand{\\endregexp}{}"));
} else if (outerDisplay() && features.inDeletedInset()
&& !features.mustProvide("ct-dvipost")) {
features.require("ct-tikz-math-sout");
}
// Validation is necessary only if not using AMS math.
@ -988,6 +1045,8 @@ void InsetMathHull::header_write(WriteStream & os) const
break;
case hullSimple:
if (os.ulemCmd())
os << "\\mbox{";
os << '$';
os.startOuterRow();
if (cell(0).empty())
@ -995,6 +1054,7 @@ void InsetMathHull::header_write(WriteStream & os) const
break;
case hullEquation:
writeMathdisplayPreamble(os);
os << "\n";
os.startOuterRow();
if (n)
@ -1008,6 +1068,7 @@ void InsetMathHull::header_write(WriteStream & os) const
case hullFlAlign:
case hullGather:
case hullMultline:
writeMathdisplayPreamble(os);
os << "\n";
os.startOuterRow();
os << "\\begin{" << hullName(type_) << star(n) << "}\n";
@ -1052,6 +1113,8 @@ void InsetMathHull::footer_write(WriteStream & os) const
case hullSimple:
os << '$';
if (os.ulemCmd())
os << "}";
break;
case hullEquation:
@ -1061,15 +1124,22 @@ void InsetMathHull::footer_write(WriteStream & os) const
os << "\\end{equation" << star(n) << "}\n";
else
os << "\\]\n";
writeMathdisplayPostamble(os);
break;
case hullEqnArray:
case hullAlign:
case hullFlAlign:
case hullAlignAt:
case hullXAlignAt:
case hullGather:
case hullMultline:
os << "\n";
os.startOuterRow();
os << "\\end{" << hullName(type_) << star(n) << "}\n";
writeMathdisplayPostamble(os);
break;
case hullAlignAt:
case hullXAlignAt:
os << "\n";
os.startOuterRow();
os << "\\end{" << hullName(type_) << star(n) << "}\n";

View File

@ -86,6 +86,8 @@ public:
///
bool ams() const;
///
bool outerDisplay() const;
///
void validate(LaTeXFeatures & features) const;
/// identifies HullInset
InsetMathHull const * asHullInset() const { return this; }

View File

@ -48,6 +48,7 @@
#include "Encoding.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "LaTeXFeatures.h"
#include "LyX.h"
#include "LyXRC.h"
#include "OutputParams.h"
@ -406,6 +407,18 @@ void InsetMathNest::latex(otexstream & os, OutputParams const & runparams) const
WriteStream wi(os, runparams.moving_arg, true,
runparams.dryrun ? WriteStream::wsDryrun : WriteStream::wsDefault,
runparams.encoding);
wi.strikeoutMath(runparams.inDeletedInset
&& (!LaTeXFeatures::isAvailable("dvipost")
|| (runparams.flavor != OutputParams::LATEX
&& runparams.flavor != OutputParams::DVILUATEX)));
if (runparams.inulemcmd) {
wi.ulemCmd(WriteStream::UNDERLINE);
if (runparams.local_font) {
FontInfo f = runparams.local_font->fontInfo();
if (f.strikeout() == FONT_ON)
wi.ulemCmd(WriteStream::STRIKEOUT);
}
}
wi.canBreakLine(os.canBreakLine());
if (runparams.lastid != -1) {
wi.pushRowEntry(os.texrow().textEntry(runparams.lastid,

View File

@ -127,7 +127,7 @@ WriteStream::WriteStream(otexrowstream & os, bool fragile, bool latex,
: os_(os), fragile_(fragile), firstitem_(false), latex_(latex),
output_(output), pendingspace_(false), pendingbrace_(false),
textmode_(false), locked_(0), ascii_(0), canbreakline_(true),
line_(0), encoding_(encoding)
mathsout_(false), ulemcmd_(NONE), line_(0), encoding_(encoding)
{}

View File

@ -40,6 +40,12 @@ public:
wsPreview
};
///
enum UlemCmdType {
NONE,
UNDERLINE,
STRIKEOUT
};
///
WriteStream(otexrowstream & os, bool fragile, bool latex, OutputType output,
Encoding const * encoding = 0);
///
@ -66,6 +72,14 @@ public:
void canBreakLine(bool breakline) { canbreakline_ = breakline; }
/// tell whether we can write an immediately following newline char
bool canBreakLine() const { return canbreakline_; }
/// record whether we have to take care for striking out display math
void strikeoutMath(bool mathsout) { mathsout_ = mathsout; }
/// tell whether we have to take care for striking out display math
bool strikeoutMath() const { return mathsout_; }
/// record which ulem command type we are inside
void ulemCmd(UlemCmdType ulemcmd) { ulemcmd_ = ulemcmd; }
/// tell which ulem command type we are inside
UlemCmdType ulemCmd() const { return ulemcmd_; }
/// writes space if next thing is isalpha()
void pendingSpace(bool how);
/// writes space if next thing is isalpha()
@ -120,6 +134,10 @@ private:
bool ascii_;
/// are we allowed to output an immediately following newline?
bool canbreakline_;
/// should we take care for striking out display math?
bool mathsout_;
/// what ulem command are we inside (none, underline, strikeout)?
UlemCmdType ulemcmd_;
///
int line_;
///

View File

@ -73,6 +73,9 @@ What's new
- Fix LaTeX output of fixed-width cells with decimal separator (bug 9568).
- Fix strike out of deleted display math with track-changes and pdf
output (bug 9678).
- Do not hardcode required packages for Note inset.
- Make *-lyxformat-* backup files use .lyx~ extension.