]> git.lyx.org Git - lyx.git/blobdiff - src/output_xhtml.cpp
whitespace again
[lyx.git] / src / output_xhtml.cpp
index 336f5a88dbcadf0b063e3c0766f35f7e1deb85fb..0a0bfd3cbe328f105941b066eaf53e0552851a7d 100644 (file)
@@ -36,6 +36,9 @@
 
 #include <vector>
 
+// Uncomment to activate debugging code.
+// #define XHTML_DEBUG
+
 using namespace std;
 using namespace lyx::support;
 
@@ -71,7 +74,8 @@ docstring escapeChar(char_type c, XHTMLStream::EscapeSettings e)
 
 
 // escape what needs escaping
-docstring htmlize(docstring const & str, XHTMLStream::EscapeSettings e) {
+docstring htmlize(docstring const & str, XHTMLStream::EscapeSettings e)
+{
        odocstringstream d;
        docstring::const_iterator it = str.begin();
        docstring::const_iterator en = str.end();
@@ -109,7 +113,8 @@ string escapeChar(char c, XHTMLStream::EscapeSettings e)
 
 
 // escape what needs escaping
-string htmlize(string const & str, XHTMLStream::EscapeSettings e) {
+string htmlize(string const & str, XHTMLStream::EscapeSettings e)
+{
        ostringstream d;
        string::const_iterator it = str.begin();
        string::const_iterator en = str.end();
@@ -143,14 +148,7 @@ docstring cleanAttr(docstring const & str)
 }
 
 
-bool isFontTag(string const & s)
-{
-       // others?
-       return s == "em" || s == "strong" || s == "i" || s == "b";
-}
-
-
-docstring StartTag::asTag() const
+docstring StartTag::writeTag() const
 {
        string output = "<" + tag_;
        if (!attr_.empty())
@@ -160,21 +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 CompTag::asTag() const
+docstring ParTag::writeTag() const
+{
+       docstring output = StartTag::writeTag();
+
+       if (parid_.empty())
+               return output;
+
+       string const pattr = "id='" + parid_ + "'";
+       output += html::CompTag("a", pattr).writeTag();
+       return output;
+}
+
+
+docstring CompTag::writeTag() const
 {
        string output = "<" + tag_;
        if (!attr_.empty())
@@ -183,6 +200,129 @@ 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_UPRIGHT:
+       case FT_SLANTED:
+       case FT_SMALLCAPS:
+       case FT_ROMAN:
+       case FT_SANS:
+       case FT_TYPE:
+       case FT_SIZE_TINY:
+       case FT_SIZE_SCRIPT:
+       case FT_SIZE_FOOTNOTE:
+       case FT_SIZE_SMALL:
+       case FT_SIZE_NORMAL:
+       case FT_SIZE_LARGE:
+       case FT_SIZE_LARGER:
+       case FT_SIZE_LARGEST:
+       case FT_SIZE_HUGE:
+       case FT_SIZE_HUGER:
+       case FT_SIZE_INCREASE:
+       case FT_SIZE_DECREASE:
+               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_UPRIGHT:
+               return html::StartTag(tag, "style='font-style:normal;'");
+       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_TYPE:
+               return html::StartTag(tag, "style='font-family:monospace;'");
+       case FT_SIZE_TINY:
+       case FT_SIZE_SCRIPT:
+       case FT_SIZE_FOOTNOTE:
+               return html::StartTag(tag, "style='font-size:x-small;'");
+       case FT_SIZE_SMALL:
+               return html::StartTag(tag, "style='font-size:small;'");
+       case FT_SIZE_NORMAL:
+               return html::StartTag(tag, "style='font-size:normal;'");
+       case FT_SIZE_LARGE:
+               return html::StartTag(tag, "style='font-size:large;'");
+       case FT_SIZE_LARGER:
+       case FT_SIZE_LARGEST:
+               return html::StartTag(tag, "style='font-size:x-large;'");
+       case FT_SIZE_HUGE:
+       case FT_SIZE_HUGER:
+               return html::StartTag(tag, "style='font-size:xx-large;'");
+       case FT_SIZE_INCREASE:
+               return html::StartTag(tag, "style='font-size:larger;'");
+       case FT_SIZE_DECREASE:
+               return html::StartTag(tag, "style='font-size:smaller;'");
+       }
+       // 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
 
 
@@ -193,12 +333,12 @@ docstring CompTag::asTag() const
 ///
 ////////////////////////////////////////////////////////////////
 
-XHTMLStream::XHTMLStream(odocstream & os) 
-               : os_(os), escape_(ESCAPE_ALL)
+XHTMLStream::XHTMLStream(odocstream & os)
+  : os_(os), escape_(ESCAPE_ALL)
 {}
 
 
-#if 0
+#ifdef XHTML_DEBUG
 void XHTMLStream::dumpTagStack(string const & msg) const
 {
        writeError(msg + ": Tag Stack");
@@ -227,7 +367,7 @@ void XHTMLStream::writeError(std::string const & s) const
 
 namespace {
        // an illegal tag for internal use
-       static string const parsep_tag = "&LyX_parsep_tag&";
+       static html::StartTag const parsep_tag("&LyX_parsep_tag&");
 }
 
 
@@ -243,31 +383,28 @@ bool XHTMLStream::closeFontTags()
                return true;
 
        // first, we close any open font tags we can close
-       html::StartTag curtag = tag_stack_.back();
-       while (html::isFontTag(curtag.tag_)) {
-               os_ << curtag.asEndTag();
+       TagPtr curtag = tag_stack_.back();
+       while (curtag->asFontTag()) {
+               os_ << curtag->writeEndTag();
                tag_stack_.pop_back();
-               if (tag_stack_.empty())
-                       // this probably shouldn't happen, since then the
-                       // font tags weren't in any other tag. but that
-                       // problem will likely be caught elsewhere.
-                       return true;
+               // this shouldn't happen, since then the font tags
+               // weren't in any other tag.
+               LBUFERR(!tag_stack_.empty());
                curtag = tag_stack_.back();
        }
        
-       if (curtag.tag_ == parsep_tag)
+       if (*curtag == parsep_tag)
                return true;
 
        // so we've hit a non-font tag.
        writeError("Tags still open in closeFontTags(). Probably not a problem,\n"
                   "but you might want to check these tags:");
-       TagStack::const_reverse_iterator it = tag_stack_.rbegin();
-       TagStack::const_reverse_iterator const en = tag_stack_.rend();
+       TagDeque::const_reverse_iterator it = tag_stack_.rbegin();
+       TagDeque::const_reverse_iterator const en = tag_stack_.rend();
        for (; it != en; ++it) {
-               string const tagname = it->tag_;
-               if (tagname == parsep_tag)
+               if (**it == parsep_tag)
                        break;
-               writeError(it->tag_);
+               writeError((*it)->tag_);
        }
        return false;
 }
@@ -275,7 +412,7 @@ bool XHTMLStream::closeFontTags()
 
 void XHTMLStream::startParagraph(bool keep_empty)
 {
-       pending_tags_.push_back(html::StartTag(parsep_tag));
+       pending_tags_.push_back(makeTagPtr(html::StartTag(parsep_tag)));
        if (keep_empty)
                clearTagDeque();
 }
@@ -290,10 +427,9 @@ void XHTMLStream::endParagraph()
                        // clear all pending tags up to and including the parsep tag.
                        // note that we work from the back, because we want to get rid
                        // of everything that hasn't been used.
-                       html::StartTag const cur_tag = pending_tags_.back();
-                       string const & tag = cur_tag.tag_;
+                       TagPtr const cur_tag = pending_tags_.back();
                        pending_tags_.pop_back();
-                       if (tag == parsep_tag)
+                       if (*cur_tag == parsep_tag)
                                break;
                }
                return;
@@ -307,13 +443,12 @@ void XHTMLStream::endParagraph()
        // this case is also normal, if the parsep tag is the last one 
        // on the stack. otherwise, it's an error.
        while (!tag_stack_.empty()) {
-               html::StartTag const cur_tag = tag_stack_.back();
-               string const & tag = cur_tag.tag_;
+               TagPtr const cur_tag = tag_stack_.back();
                tag_stack_.pop_back();
-               if (tag == parsep_tag)
+               if (*cur_tag == parsep_tag)
                        break;
-               writeError("Tag `" + tag + "' still open at end of paragraph. Closing.");
-               os_ << cur_tag.asEndTag();
+               writeError("Tag `" + cur_tag->tag_ + "' still open at end of paragraph. Closing.");
+               os_ << cur_tag->writeEndTag();
        }
 }
 
@@ -321,10 +456,10 @@ void XHTMLStream::endParagraph()
 void XHTMLStream::clearTagDeque()
 {
        while (!pending_tags_.empty()) {
-               html::StartTag const & tag = pending_tags_.front();
-               if (tag.tag_ != parsep_tag)
+               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();
        }
@@ -388,25 +523,42 @@ XHTMLStream & XHTMLStream::operator<<(html::StartTag const & tag)
 {
        if (tag.tag_.empty())
                return *this;
-       pending_tags_.push_back(tag);
+       pending_tags_.push_back(makeTagPtr(tag));
        if (tag.keepempty_)
                clearTagDeque();
        return *this;
 }
 
 
+XHTMLStream & XHTMLStream::operator<<(html::ParTag const & tag)
+{
+       if (tag.tag_.empty())
+               return *this;
+       pending_tags_.push_back(makeTagPtr(tag));
+       return *this;
+}
+
+
 XHTMLStream & XHTMLStream::operator<<(html::CompTag const & tag) 
 {
        if (tag.tag_.empty())
                return *this;
        clearTagDeque();
-       // tabs?
-       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?
@@ -415,23 +567,34 @@ XHTMLStream & XHTMLStream::operator<<(html::CR const &)
 }
 
 
-bool XHTMLStream::isTagOpen(string const & stag) const
+bool XHTMLStream::isTagOpen(html::StartTag const & stag) const
+{
+       TagDeque::const_iterator sit = tag_stack_.begin();
+       TagDeque::const_iterator const sen = tag_stack_.end();
+       for (; sit != sen; ++sit)
+               if (**sit == stag)
+                       return true;
+       return false;
+}
+
+
+bool XHTMLStream::isTagOpen(html::EndTag const & etag) const
 {
-       TagStack::const_iterator sit = tag_stack_.begin();
-       TagStack::const_iterator const sen = tag_stack_.end();
+       TagDeque::const_iterator sit = tag_stack_.begin();
+       TagDeque::const_iterator const sen = tag_stack_.end();
        for (; sit != sen; ++sit)
-               if (sit->tag_ == stag) 
+               if (etag == **sit)
                        return true;
        return false;
 }
 
 
-bool XHTMLStream::isTagPending(string const & stag) const
+bool XHTMLStream::isTagPending(html::StartTag const & stag) const
 {
-       TagStack::const_iterator sit = pending_tags_.begin();
-       TagStack::const_iterator const sen = pending_tags_.end();
+       TagDeque::const_iterator sit = pending_tags_.begin();
+       TagDeque::const_iterator const sen = pending_tags_.end();
        for (; sit != sen; ++sit)
-               if (sit->tag_ == stag)
+               if (**sit == stag)
                        return true;
        return false;
 }
@@ -447,40 +610,36 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
        if (etag.tag_.empty())
                return *this;
 
-       // make sure there are tags to be closed
-       if (tag_stack_.empty()) {
-               writeError("Tried to close `" + etag.tag_
-                        + "' when no tags were open!");
-               return *this;           
-       }
-
-       // first make sure we're not closing an empty tag
+       // if this tag is pending, we can simply discard it.
        if (!pending_tags_.empty()) {
-               html::StartTag const & stag = pending_tags_.back();
-               if (etag.tag_ == stag.tag_)  {
+
+               if (etag == *pending_tags_.back()) {
                        // we have <tag></tag>, so we discard it and remove it 
                        // from the pending_tags_.
                        pending_tags_.pop_back();
                        return *this;
                }
+
                // there is a pending tag that isn't the one we are trying
                // to close. 
+
                // is this tag itself pending?
                // non-const iterators because we may call erase().
-               TagStack::iterator dit = pending_tags_.begin();
-               TagStack::iterator const den = pending_tags_.end();
+               TagDeque::iterator dit = pending_tags_.begin();
+               TagDeque::iterator const den = pending_tags_.end();
                for (; dit != den; ++dit) {
-                       if (dit->tag_ == etag.tag_) {
+                       if (etag == **dit) {
                                // it was pending, so we just erase it
                                writeError("Tried to close pending tag `" + etag.tag_ 
                                        + "' when other tags were pending. Last pending tag is `"
-                                       + pending_tags_.back().tag_ + "'. Tag discarded.");
+                                       + to_utf8(pending_tags_.back()->writeTag()) 
+                                       + "'. Tag discarded.");
                                pending_tags_.erase(dit);
                                return *this;
                        }
                }
                // so etag isn't itself pending. is it even open?
-               if (!isTagOpen(etag.tag_)) {
+               if (!isTagOpen(etag)) {
                        writeError("Tried to close `" + etag.tag_ 
                                 + "' when tag was not open. Tag discarded.");
                        return *this;
@@ -491,17 +650,24 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
                string estr = "Closing tag `" + etag.tag_ 
                        + "' when other tags are pending. Discarded pending tags:\n";
                for (dit = pending_tags_.begin(); dit != den; ++dit)
-                       estr += dit->tag_ + "\n";
+                       estr += to_utf8(html::htmlize((*dit)->writeTag(), XHTMLStream::ESCAPE_ALL)) + "\n";
                writeError(estr);
                // clear the pending tags...
                pending_tags_.clear();
                // ...and then just fall through.
        }
 
+       // make sure there are tags to be closed
+       if (tag_stack_.empty()) {
+               writeError("Tried to close `" + etag.tag_
+                        + "' when no tags were open!");
+               return *this;           
+       }
+
        // is the tag we are closing the last one we opened?
-       if (etag.tag_ == tag_stack_.back().tag_) {
+       if (etag == *tag_stack_.back()) {
                // output it...
-               os_ << etag.asEndTag();
+               os_ << etag.writeEndTag();
                // ...and forget about it
                tag_stack_.pop_back();
                return *this;
@@ -509,7 +675,7 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
        
        // we are trying to close a tag other than the one last opened. 
        // let's first see if this particular tag is still open somehow.
-       if (!isTagOpen(etag.tag_)) {
+       if (!isTagOpen(etag)) {
                writeError("Tried to close `" + etag.tag_ 
                        + "' when tag was not open. Tag discarded.");
                return *this;
@@ -518,18 +684,18 @@ 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.
-               TagStack::const_reverse_iterator rit = tag_stack_.rbegin();
-               TagStack::const_reverse_iterator ren = tag_stack_.rend();
+               TagDeque::const_reverse_iterator rit = tag_stack_.rbegin();
+               TagDeque::const_reverse_iterator ren = tag_stack_.rend();
                for (; rit != ren; ++rit) {
-                       if (rit->tag_ == etag.tag_)
+                       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_ + "'.");
+                                       + "' due to open non-font tag `" + (*rit)->tag_ + "'.");
                                return *this;
                        }
                }
@@ -539,17 +705,16 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
                // and are being asked to closed em. we want:
                //    <em>this is <strong>bold</strong></em><strong>
                // first, we close the intervening tags...
-               html::StartTag curtag = tag_stack_.back();
+               TagPtr curtag = tag_stack_.back();
                // ...remembering them in a stack.
-               TagStack fontstack;
-               while (curtag.tag_ != etag.tag_) {
-                       os_ << curtag.asEndTag();
+               TagDeque fontstack;
+               while (etag != *curtag) {
+                       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.
@@ -566,44 +731,46 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
        // at least guarantees proper nesting.
        writeError("Closing tag `" + etag.tag_ 
                + "' when other tags are open, namely:");
-       html::StartTag curtag = tag_stack_.back();
-       while (curtag.tag_ != etag.tag_) {
-               writeError(curtag.tag_);
-               if (curtag.tag_ != parsep_tag)
-                       os_ << curtag.asEndTag();
+       TagPtr curtag = tag_stack_.back();
+       while (etag != *curtag) {
+               writeError(curtag->tag_);
+               if (*curtag != parsep_tag)
+                       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;
 }
 
 // End code for XHTMLStream
 
 namespace {
-       
+
 // convenience functions
 
-inline void openTag(XHTMLStream & xs, Layout const & lay)
+inline void openParTag(XHTMLStream & xs, Layout const & lay,
+                       std::string parlabel)
 {
-       xs << html::StartTag(lay.htmltag(), lay.htmlattr());
+       xs << html::ParTag(lay.htmltag(), lay.htmlattr(), parlabel);
 }
 
 
-void openTag(XHTMLStream & xs, Layout const & lay, 
-             ParagraphParameters const & params)
+void openParTag(XHTMLStream & xs, Layout const & lay,
+                ParagraphParameters const & params,
+                std::string parlabel)
 {
        // FIXME Are there other things we should handle here?
        string const align = alignmentToCSS(params.align());
        if (align.empty()) {
-               openTag(xs, lay);
+               openParTag(xs, lay, parlabel);
                return;
        }
        string attrs = lay.htmlattr() + " style='text-align: " + align + ";'";
-       xs << html::StartTag(lay.htmltag(), attrs);
+       xs << html::ParTag(lay.htmltag(), attrs, parlabel);
 }
 
 
@@ -652,7 +819,7 @@ inline void closeItemTag(XHTMLStream & xs, Layout const & lay)
 
 // end of convenience functions
 
-ParagraphList::const_iterator searchParagraphHtml(
+ParagraphList::const_iterator findLastParagraph(
        ParagraphList::const_iterator p,
        ParagraphList::const_iterator const & pend)
 {
@@ -663,8 +830,8 @@ ParagraphList::const_iterator searchParagraphHtml(
 }
 
 
-ParagraphList::const_iterator searchEnvironmentHtml(
-               ParagraphList::const_iterator const pstart,
+ParagraphList::const_iterator findEndOfEnvironment(
+               ParagraphList::const_iterator const pstart,
                ParagraphList::const_iterator const & pend)
 {
        ParagraphList::const_iterator p = pstart;
@@ -677,15 +844,19 @@ ParagraphList::const_iterator searchEnvironmentHtml(
                // it can happen. We pretend that it's just at lowest depth.
                if (style.latextype == LATEX_COMMAND)
                        return p;
+
                // If depth is down, we're done
                if (p->params().depth() < depth)
                        return p;
+
                // If depth is up, we're not done
                if (p->params().depth() > depth)
                        continue;
-               // Now we know we are at the same depth
-               if (style.latextype == LATEX_PARAGRAPH
-                   || style.latexname() != bstyle.latexname())
+
+               // FIXME I am not sure about the first check.
+               // Surely we *could* have different layouts that count as
+               // LATEX_PARAGRAPH, right? 
+               if (style.latextype == LATEX_PARAGRAPH || style != bstyle)
                        return p;
        }
        return pend;
@@ -704,7 +875,8 @@ ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
        for (; par != pend; ++par) {
                Layout const & lay = par->layout();
                if (!lay.counter.empty())
-                       buf.params().documentClass().counters().step(lay.counter, OutputUpdate);
+                       buf.masterBuffer()->params().
+                           documentClass().counters().step(lay.counter, OutputUpdate);
                // FIXME We should see if there's a label to be output and
                // do something with it.
                if (par != pbegin)
@@ -716,8 +888,12 @@ ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
                // multiple paragraphs.
                bool const opened = runparams.html_make_pars &&
                        (par != pbegin || !runparams.html_in_par);
+               bool const make_parid = !runparams.for_toc && runparams.html_make_pars;
+
                if (opened)
-                       openTag(xs, lay, par->params());
+                       openParTag(xs, lay, par->params(),
+                                  make_parid ? par->magicLabel() : "");
+
                docstring const deferred = 
                        par->simpleLyXHTMLOnePar(buf, xs, runparams, text.outerFont(distance(begin, par)));
 
@@ -771,7 +947,7 @@ bool isNormalEnv(Layout const & lay)
 }
 
        
-ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
+ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
                                              XHTMLStream & xs,
                                              OutputParams const & runparams,
                                              Text const & text,
@@ -784,7 +960,7 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
        depth_type const origdepth = pbegin->params().depth();
 
        // open tag for this environment
-       openTag(xs, bstyle);
+       openParTag(xs, bstyle, pbegin->magicLabel());
        xs << html::CR();
 
        // we will on occasion need to remember a layout from before.
@@ -798,7 +974,7 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
                // FIXME There may be a bug here about user defined enumeration
                // types. If so, then we'll need to take the counter and add "i",
                // "ii", etc, as with enum.
-               Counters & cnts = buf.params().documentClass().counters();
+               Counters & cnts = buf.masterBuffer()->params().documentClass().counters();
                docstring const & cntr = style.counter;
                if (!style.counter.empty() 
                    && (par == pbegin || !isNormalEnv(style)) 
@@ -818,7 +994,7 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
                        // One is that we are still in the environment in which we 
                        // started---which we will be if the depth is the same.
                        if (par->params().depth() == origdepth) {
-                               LASSERT(bstyle == style, /* */);
+                               LATTEST(bstyle == style);
                                if (lastlay != 0) {
                                        closeItemTag(xs, *lastlay);
                                        lastlay = 0;
@@ -884,13 +1060,13 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
                        // The other possibility is that the depth has increased, in which
                        // case we need to recurse.
                        else {
-                               send = searchEnvironmentHtml(par, pend);
-                               par = makeEnvironmentHtml(buf, xs, runparams, text, par, send);
+                               send = findEndOfEnvironment(par, pend);
+                               par = makeEnvironment(buf, xs, runparams, text, par, send);
                        }
                        break;
                }
                case LATEX_PARAGRAPH:
-                       send = searchParagraphHtml(par, pend);
+                       send = findLastParagraph(par, pend);
                        par = makeParagraphs(buf, xs, runparams, text, par, send);
                        break;
                // Shouldn't happen
@@ -915,19 +1091,24 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
 
 
 void makeCommand(Buffer const & buf,
-                                         XHTMLStream & xs,
-                                         OutputParams const & runparams,
-                                         Text const & text,
-                                         ParagraphList::const_iterator const & pbegin)
+                XHTMLStream & xs,
+                OutputParams const & runparams,
+                Text const & text,
+                ParagraphList::const_iterator const & pbegin)
 {
        Layout const & style = pbegin->layout();
        if (!style.counter.empty())
-               buf.params().documentClass().counters().step(style.counter, OutputUpdate);
+               buf.masterBuffer()->params().
+                   documentClass().counters().step(style.counter, OutputUpdate);
 
-       openTag(xs, style, pbegin->params());
+       bool const make_parid = !runparams.for_toc && runparams.html_make_pars;
+
+       openParTag(xs, style, pbegin->params(),
+                  make_parid ? pbegin->magicLabel() : "");
 
        // Label around sectioning number:
        // FIXME Probably need to account for LABEL_MANUAL
+       // FIXME Probably also need now to account for labels ABOVE and CENTERED.
        if (style.labeltype != LABEL_NO_LABEL) {
                openLabelTag(xs, style);
                xs << pbegin->params().labelString();
@@ -958,7 +1139,8 @@ void xhtmlParagraphs(Text const & text,
        }
        pit_type bpit = runparams.par_begin;
        pit_type const epit = runparams.par_end;
-       LASSERT(bpit < epit, /* */);
+       LASSERT(bpit < epit,
+               { xs << XHTMLStream::ESCAPE_NONE << "<!-- XHTML output error! -->\n"; return; });
 
        OutputParams ourparams = runparams;
        ParagraphList::const_iterator const pend =
@@ -1000,18 +1182,18 @@ void xhtmlParagraphs(Text const & text,
                case LATEX_LIST_ENVIRONMENT:
                case LATEX_ITEM_ENVIRONMENT: {
                        // FIXME Same fix here.
-                       send = searchEnvironmentHtml(par, pend);
-                       par = makeEnvironmentHtml(buf, xs, ourparams, text, par, send);
+                       send = findEndOfEnvironment(par, pend);
+                       par = makeEnvironment(buf, xs, ourparams, text, par, send);
                        break;
                }
                case LATEX_BIB_ENVIRONMENT: {
                        // FIXME Same fix here.
-                       send = searchEnvironmentHtml(par, pend);
+                       send = findEndOfEnvironment(par, pend);
                        par = makeBibliography(buf, xs, ourparams, text, par, send);
                        break;
                }
                case LATEX_PARAGRAPH:
-                       send = searchParagraphHtml(par, pend);
+                       send = findLastParagraph(par, pend);
                        par = makeParagraphs(buf, xs, ourparams, text, par, send);
                        break;
                }
@@ -1020,7 +1202,8 @@ void xhtmlParagraphs(Text const & text,
 }
 
 
-string alignmentToCSS(LyXAlignment align) {
+string alignmentToCSS(LyXAlignment align)
+{
        switch (align) {
        case LYX_ALIGN_BLOCK:
                // we are NOT going to use text-align: justify!!