From 5cadeed4d7a2c8e7d64f1e774d6dea5621382539 Mon Sep 17 00:00:00 2001 From: Richard Heck Date: Tue, 7 May 2013 02:34:15 -0400 Subject: [PATCH] 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. --- src/Paragraph.cpp | 38 +++++------ src/output_xhtml.cpp | 150 +++++++++++++++++++++++++++++++++++-------- src/output_xhtml.h | 104 +++++++++++++++++------------- 3 files changed, 198 insertions(+), 94 deletions(-) diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 535506f9d4..a95163ef39 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -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? diff --git a/src/output_xhtml.cpp b/src/output_xhtml.cpp index cbb2c5c3d4..7212bb2de1 100644 --- a/src/output_xhtml.cpp +++ b/src/output_xhtml.cpp @@ -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 = ""; return from_utf8(output); } -docstring EndTag::asEndTag() const +bool StartTag::operator==(FontTag const & rhs) const +{ + return rhs == *this; +} + + +docstring EndTag::writeEndTag() const { string output = ""; 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; diff --git a/src/output_xhtml.h b/src/output_xhtml.h index 0bcc9849c0..c825a4d805 100644 --- a/src/output_xhtml.h +++ b/src/output_xhtml.h @@ -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() {} /// - virtual docstring asTag() const; + virtual docstring writeTag() const; /// - 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) {} + /// + 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 +/// 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) {} + /// + 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) {} - /// - 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 -/// 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) {} - /// - 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 {