Rework that way that font tags are handled in XHTML output. We need

to do this in order to handle span tags properly, when they act as
font tags.
This commit is contained in:
Richard Heck 2013-05-07 02:34:15 -04:00
parent 64f0cffbba
commit 5cadeed4d7
3 changed files with 198 additions and 94 deletions

View File

@ -2831,17 +2831,19 @@ void Paragraph::simpleDocBookOnePar(Buffer const & buf,
}
namespace {
void doFontSwitch(XHTMLStream & xs, bool startrange,
bool & flag, FontState curstate, std::string tag, std::string attr = "")
bool & flag, FontState curstate, html::FontTypes type)
{
if (curstate == FONT_ON) {
xs << html::StartTag(tag, attr);
xs << html::FontTag(type);
flag = true;
} else if (flag && !startrange) {
xs << html::EndTag(tag);
xs << html::EndFontTag(type);
flag = false;
}
}
}
docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
@ -2879,47 +2881,39 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
// emphasis
FontState curstate = font.fontInfo().emph();
if (font_old.emph() != curstate)
doFontSwitch(xs, at_start, emph_flag, curstate, "em");
doFontSwitch(xs, at_start, emph_flag, curstate, html::FT_EMPH);
// noun
curstate = font.fontInfo().noun();
if (font_old.noun() != curstate)
doFontSwitch(xs, at_start, noun_flag, curstate, "dfn", "class='lyxnoun'");
doFontSwitch(xs, at_start, noun_flag, curstate, html::FT_NOUN);
// underbar
curstate = font.fontInfo().underbar();
if (font_old.underbar() != curstate)
doFontSwitch(xs, at_start, ubar_flag, curstate, "u");
doFontSwitch(xs, at_start, ubar_flag, curstate, html::FT_UBAR);
// strikeout
curstate = font.fontInfo().strikeout();
if (font_old.strikeout() != curstate)
doFontSwitch(xs, at_start, sout_flag, curstate, "del", "class='strikeout'");
// HTML does not really have an equivalent of the next two, so we will just
// output a single underscore with a class, and people can style it if they
// wish to do so
doFontSwitch(xs, at_start, sout_flag, curstate, html::FT_SOUT);
// double underbar
curstate = font.fontInfo().uuline();
if (font_old.uuline() != curstate)
doFontSwitch(xs, at_start, dbar_flag, curstate, "u", "class='dline'");
doFontSwitch(xs, at_start, dbar_flag, curstate, html::FT_DBAR);
// wavy line
curstate = font.fontInfo().uwave();
if (font_old.uwave() != curstate)
doFontSwitch(xs, at_start, wave_flag, curstate, "u", "class='wavyline'");
doFontSwitch(xs, at_start, wave_flag, curstate, html::FT_WAVE);
// bold
if (font_old.series() != font.fontInfo().series()) {
if (font.fontInfo().series() == BOLD_SERIES) {
xs << html::StartTag("b");
bold_flag = true;
} else if (bold_flag && !at_start) {
xs << html::EndTag("b");
bold_flag = false;
}
}
// 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(xs, at_start, bold_flag, curstate, html::FT_BOLD);
// FIXME XHTML
// Other such tags? What about the other text ranges?

View File

@ -148,15 +148,7 @@ docstring cleanAttr(docstring const & str)
}
bool isFontTag(string const & s)
{
return s == "em" || s == "strong" || s == "i" || s == "b"
|| s == "dfn" || s == "kbd" || s == "var" || s == "samp"
|| s == "del" || s == "u";
}
docstring StartTag::asTag() const
docstring StartTag::writeTag() const
{
string output = "<" + tag_;
if (!attr_.empty())
@ -166,34 +158,40 @@ docstring StartTag::asTag() const
}
docstring StartTag::asEndTag() const
docstring StartTag::writeEndTag() const
{
string output = "</" + tag_ + ">";
return from_utf8(output);
}
docstring EndTag::asEndTag() const
bool StartTag::operator==(FontTag const & rhs) const
{
return rhs == *this;
}
docstring EndTag::writeEndTag() const
{
string output = "</" + tag_ + ">";
return from_utf8(output);
}
docstring ParTag::asTag() const
docstring ParTag::writeTag() const
{
docstring output = StartTag::asTag();
docstring output = StartTag::writeTag();
if (parid_.empty())
return output;
string const pattr = "id='" + parid_ + "'";
output += html::CompTag("a", pattr).asTag();
output += html::CompTag("a", pattr).writeTag();
return output;
}
docstring CompTag::asTag() const
docstring CompTag::writeTag() const
{
string output = "<" + tag_;
if (!attr_.empty())
@ -202,6 +200,94 @@ docstring CompTag::asTag() const
return from_utf8(output);
}
namespace {
string fontToTag(html::FontTypes type)
{
switch(type) {
case FT_EMPH:
return "em";
case FT_BOLD:
return "b";
case FT_NOUN:
return "dfn";
case FT_UBAR:
case FT_WAVE:
case FT_DBAR:
return "u";
case FT_SOUT:
return "del";
case FT_ITALIC:
return "i";
case FT_SLANTED:
case FT_SMALLCAPS:
case FT_ROMAN:
case FT_SANS:
case FT_TYPER:
return "span";
}
// kill warning
return "";
}
StartTag fontToStartTag(html::FontTypes type)
{
string tag = fontToTag(type);
switch(type) {
case FT_EMPH:
return html::StartTag(tag);
case FT_BOLD:
return html::StartTag(tag);
case FT_NOUN:
return html::StartTag(tag, "class='lyxnoun'");
case FT_UBAR:
return html::StartTag(tag);
case FT_DBAR:
return html::StartTag(tag, "class='dline'");
case FT_SOUT:
return html::StartTag(tag, "class='strikeout'");
case FT_WAVE:
return html::StartTag(tag, "class='wline'");
case FT_ITALIC:
return html::StartTag(tag);
case FT_SLANTED:
return html::StartTag(tag, "style='font-style:oblique;'");
case FT_SMALLCAPS:
return html::StartTag(tag, "style='font-variant:small-caps;'");
case FT_ROMAN:
return html::StartTag(tag, "style='font-family:serif;'");
case FT_SANS:
return html::StartTag(tag, "style='font-family:sans-serif;'");
case FT_TYPER:
return html::StartTag(tag, "style='font-family:monospace;'");
}
// kill warning
return StartTag("");
}
} // end anonymous namespace
FontTag::FontTag(FontTypes type)
: StartTag(fontToStartTag(type)), font_type_(type)
{}
bool FontTag::operator==(StartTag const & tag) const
{
FontTag const * const ftag = tag.asFontTag();
if (!ftag)
return false;
return (font_type_ == ftag->font_type_);
}
EndFontTag::EndFontTag(FontTypes type)
: EndTag(fontToTag(type)), font_type_(type)
{}
} // namespace html
@ -263,8 +349,8 @@ bool XHTMLStream::closeFontTags()
// first, we close any open font tags we can close
TagPtr curtag = tag_stack_.back();
while (html::isFontTag(curtag->tag_)) {
os_ << curtag->asEndTag();
while (curtag->asFontTag()) {
os_ << curtag->writeEndTag();
tag_stack_.pop_back();
// this shouldn't happen, since then the font tags
// weren't in any other tag.
@ -327,7 +413,7 @@ void XHTMLStream::endParagraph()
if (*cur_tag == parsep_tag)
break;
writeError("Tag `" + cur_tag->tag_ + "' still open at end of paragraph. Closing.");
os_ << cur_tag->asEndTag();
os_ << cur_tag->writeEndTag();
}
}
@ -338,7 +424,7 @@ void XHTMLStream::clearTagDeque()
TagPtr const tag = pending_tags_.front();
if (*tag != parsep_tag)
// tabs?
os_ << tag->asTag();
os_ << tag->writeTag();
tag_stack_.push_back(tag);
pending_tags_.pop_front();
}
@ -423,12 +509,21 @@ XHTMLStream & XHTMLStream::operator<<(html::CompTag const & tag)
if (tag.tag_.empty())
return *this;
clearTagDeque();
os_ << tag.asTag();
os_ << tag.writeTag();
*this << html::CR();
return *this;
}
XHTMLStream & XHTMLStream::operator<<(html::FontTag const & tag)
{
if (tag.tag_.empty())
return *this;
pending_tags_.push_back(makeTagPtr(tag));
return *this;
}
XHTMLStream & XHTMLStream::operator<<(html::CR const &)
{
// tabs?
@ -536,7 +631,7 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
// is the tag we are closing the last one we opened?
if (etag == *tag_stack_.back()) {
// output it...
os_ << etag.asEndTag();
os_ << etag.writeEndTag();
// ...and forget about it
tag_stack_.pop_back();
return *this;
@ -553,7 +648,7 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
// so the tag was opened, but other tags have been opened since
// and not yet closed.
// if it's a font tag, though...
if (html::isFontTag(etag.tag_)) {
if (etag.asFontTag()) {
// it won't be a problem if the other tags open since this one
// are also font tags.
TagDeque::const_reverse_iterator rit = tag_stack_.rbegin();
@ -561,7 +656,7 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
for (; rit != ren; ++rit) {
if (etag == **rit)
break;
if (!html::isFontTag((*rit)->tag_)) {
if (!(*rit)->asFontTag()) {
// we'll just leave it and, presumably, have to close it later.
writeError("Unable to close font tag `" + etag.tag_
+ "' due to open non-font tag `" + (*rit)->tag_ + "'.");
@ -578,13 +673,12 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
// ...remembering them in a stack.
TagDeque fontstack;
while (etag != *curtag) {
os_ << curtag->asEndTag();
os_ << curtag->writeEndTag();
fontstack.push_back(curtag);
tag_stack_.pop_back();
curtag = tag_stack_.back();
}
// now close our tag...
os_ << etag.asEndTag();
os_ << etag.writeEndTag();
tag_stack_.pop_back();
// ...and restore the other tags.
@ -605,12 +699,12 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
while (etag != *curtag) {
writeError(curtag->tag_);
if (*curtag != parsep_tag)
os_ << curtag->asEndTag();
os_ << curtag->writeEndTag();
tag_stack_.pop_back();
curtag = tag_stack_.back();
}
// curtag is now the one we actually want.
os_ << curtag->asEndTag();
os_ << curtag->writeEndTag();
tag_stack_.pop_back();
return *this;

View File

@ -31,6 +31,9 @@ class Text;
namespace html {
class FontTag;
class EndFontTag;
/// Attributes will be escaped automatically and so should NOT
/// be escaped before being passed to the constructor.
struct StartTag
@ -44,16 +47,20 @@ struct StartTag
///
~StartTag() {}
/// <tag_ attr_>
virtual docstring asTag() const;
virtual docstring writeTag() const;
/// </tag_>
virtual docstring asEndTag() const;
virtual docstring writeEndTag() const;
///
bool operator==(StartTag const & rhs) const
virtual FontTag const * asFontTag() const { return 0; }
///
virtual bool operator==(StartTag const & rhs) const
{ return tag_ == rhs.tag_; }
///
bool operator!=(StartTag const & rhs) const
virtual bool operator!=(StartTag const & rhs) const
{ return !(*this == rhs); }
///
virtual bool operator==(FontTag const & rhs) const;
///
std::string tag_;
///
std::string attr_;
@ -63,6 +70,46 @@ struct StartTag
};
///
struct EndTag
{
///
explicit EndTag(std::string tag) : tag_(tag) {}
/// </tag_>
virtual docstring writeEndTag() const;
///
bool operator==(StartTag const & rhs) const
{ return tag_ == rhs.tag_; }
///
bool operator!=(StartTag const & rhs) const
{ return !(*this == rhs); }
///
virtual EndFontTag const * asFontTag() const { return 0; }
///
std::string tag_;
};
/// Tags like <img />
/// Attributes will be escaped automatically and so should NOT
/// be escaped before being passed to the constructor.
struct CompTag
{
///
explicit CompTag(std::string const & tag)
: tag_(tag) {}
///
explicit CompTag(std::string const & tag, std::string const & attr)
: tag_(tag), attr_(attr) {}
/// <tag_ attr_ />
docstring writeTag() const;
///
std::string tag_;
///
std::string attr_;
};
/// A special case of StartTag, used exclusively for tags that wrap paragraphs.
struct ParTag : public StartTag
{
@ -74,7 +121,7 @@ struct ParTag : public StartTag
///
~ParTag() {}
///
docstring asTag() const;
docstring writeTag() const;
/// the "magic par label" for this paragraph
std::string parid_;
};
@ -105,56 +152,23 @@ struct FontTag : public StartTag
///
explicit FontTag(FontTypes type);
///
docstring asTag() const;
FontTag const * asFontTag() const { return this; }
///
docstring asEndTag() const;
///
bool isFontTag() { return true; }
///
bool operator==(FontTag const & rhs)
{ return font_type_ == rhs.font_type_; }
/// Asserts.
bool operator==(StartTag const &);
bool operator==(StartTag const &) const;
///
FontTypes font_type_;
};
///
struct EndTag
struct EndFontTag : public EndTag
{
///
explicit EndTag(std::string tag) : tag_(tag) {}
/// </tag_>
docstring asEndTag() const;
explicit EndFontTag(FontTypes type);
///
bool operator==(StartTag const & rhs) const
{ return tag_ == rhs.tag_; }
EndFontTag const * asFontTag() const { return this; }
///
bool operator!=(StartTag const & rhs) const
{ return !(*this == rhs); }
///
std::string tag_;
};
/// Tags like <img />
/// Attributes will be escaped automatically and so should NOT
/// be escaped before being passed to the constructor.
struct CompTag
{
///
explicit CompTag(std::string const & tag)
: tag_(tag) {}
///
explicit CompTag(std::string const & tag, std::string const & attr)
: tag_(tag), attr_(attr) {}
/// <tag_ attr_ />
docstring asTag() const;
///
std::string tag_;
///
std::string attr_;
FontTypes font_type_;
};
@ -201,6 +215,8 @@ public:
///
XHTMLStream & operator<<(html::ParTag const &);
///
XHTMLStream & operator<<(html::FontTag const &);
///
XHTMLStream & operator<<(html::CR const &);
///
enum EscapeSettings {