]> git.lyx.org Git - lyx.git/blobdiff - src/output_xhtml.cpp
Routines for calculating numerical labels for BibTeX citations.
[lyx.git] / src / output_xhtml.cpp
index 6e493df6d768464141c9fa1370a745d20e148a66..fdf8681b56b8964946bbf3da2b89bbb8f2b3d412 100644 (file)
@@ -75,6 +75,66 @@ docstring htmlize(docstring const & str) {
 }
 
 
+string escapeChar(char c)
+{
+       string str;
+       switch (c) {
+       case ' ':
+               str += " ";
+               break;
+       case '&':
+               str += "&";
+               break;
+       case '<':
+               str += "&lt;";
+               break;
+       case '>':
+               str += "&gt;";
+               break;
+       default:
+               str += c;
+               break;
+       }
+       return str;
+}
+
+
+// escape what needs escaping
+string htmlize(string const & str) {
+       ostringstream d;
+       string::const_iterator it = str.begin();
+       string::const_iterator en = str.end();
+       for (; it != en; ++it)
+               d << escapeChar(*it);
+       return d.str();
+}
+
+
+string cleanAttr(string const & str)
+{
+       string newname;
+       string::const_iterator it = str.begin();
+       string::const_iterator en = str.end();
+       for (; it != en; ++it)
+               newname += isalnum(*it) ? *it : '_';
+       return newname; 
+}
+
+
+docstring cleanAttr(docstring const & str)
+{
+       docstring newname;
+       docstring::const_iterator it = str.begin();
+       docstring::const_iterator en = str.end();
+       for (; it != en; ++it)
+               if (isalnum(*it))
+                       newname += *it;
+               else
+                       newname += '_';
+       return newname; 
+}
+
+
 bool isFontTag(string const & s)
 {
        return s == "em" || s == "strong"; // others?
@@ -86,7 +146,7 @@ docstring StartTag::asTag() const
 {
        string output = "<" + tag_;
        if (!attr_.empty())
-               output += " " + attr_;
+               output += " " + html::htmlize(attr_);
        output += ">";
        return from_utf8(output);
 }
@@ -110,7 +170,7 @@ docstring CompTag::asTag() const
 {
        string output = "<" + tag_;
        if (!attr_.empty())
-               output += " " + attr_;
+               output += " " + html::htmlize(attr_);
        output += " />";
        return from_utf8(output);
 }
@@ -123,7 +183,7 @@ docstring CompTag::asTag() const
 ////////////////////////////////////////////////////////////////
 
 XHTMLStream::XHTMLStream(odocstream & os) 
-               :os_(os)
+               : os_(os), nextraw_(false)
 {}
 
 
@@ -134,8 +194,17 @@ void XHTMLStream::cr()
 }
 
 
+void XHTMLStream::writeError(std::string const & s)
+{
+       LYXERR0(s);
+       os_ << from_utf8("<!-- Output Error: " + s + " -->");
+}
+
+
 bool XHTMLStream::closeFontTags()
 {
+       if (tag_stack_.empty())
+               return true;
        // first, we close any open font tags we can close
        StartTag curtag = tag_stack_.back();
        while (html::isFontTag(curtag.tag_)) {
@@ -155,7 +224,7 @@ bool XHTMLStream::closeFontTags()
        bool noFontTags = true;
        for (; it != en; ++it) {
                if (html::isFontTag(it->tag_)) {
-                       LYXERR0("Font tag `" << it->tag_ << "' still open in closeFontTags().");
+                       writeError("Font tag `" + it->tag_ + "' still open in closeFontTags().");
                        noFontTags = false;
                }
        }
@@ -178,7 +247,11 @@ void XHTMLStream::clearTagDeque()
 XHTMLStream & XHTMLStream::operator<<(docstring const & d)
 {
        clearTagDeque();
-       os_ << html::htmlize(d);
+       if (nextraw_) {
+               os_ << d;
+               nextraw_ = false;
+       } else
+               os_ << html::htmlize(d);
        return *this;
 }
 
@@ -186,7 +259,12 @@ XHTMLStream & XHTMLStream::operator<<(docstring const & d)
 XHTMLStream & XHTMLStream::operator<<(const char * s)
 {
        clearTagDeque();
-       os_ << html::htmlize(from_ascii(s));
+       docstring const d = from_ascii(s);
+       if (nextraw_) {
+               os_ << d;
+               nextraw_ = false;
+       } else
+               os_ << html::htmlize(d);
        return *this;
 }
 
@@ -194,13 +272,26 @@ XHTMLStream & XHTMLStream::operator<<(const char * s)
 XHTMLStream & XHTMLStream::operator<<(char_type c)
 {
        clearTagDeque();
-       os_ << html::escapeChar(c);
+       if (nextraw_) {
+               os_ << c;
+               nextraw_ = false;
+       } else
+               os_ << html::escapeChar(c);
+       return *this;
+}
+
+
+XHTMLStream & XHTMLStream::operator<<(NextRaw const &) 
+{ 
+       nextraw_ = true; 
        return *this;
 }
 
 
 XHTMLStream & XHTMLStream::operator<<(StartTag const & tag) 
 {
+       if (tag.tag_.empty())
+               return *this;
        pending_tags_.push_back(tag);
        if (tag.keepempty_)
                clearTagDeque();
@@ -210,14 +301,17 @@ XHTMLStream & XHTMLStream::operator<<(StartTag const & tag)
 
 XHTMLStream & XHTMLStream::operator<<(CompTag const & tag) 
 {
+       if (tag.tag_.empty())
+               return *this;
        clearTagDeque();
        // tabs?
        os_ << tag.asTag();
+       cr();
        return *this;
 }
 
 
-bool   XHTMLStream::isTagOpen(string const & stag)
+bool XHTMLStream::isTagOpen(string const & stag)
 {
        TagStack::const_iterator sit = tag_stack_.begin();
        TagStack::const_iterator const sen = tag_stack_.end();
@@ -236,6 +330,8 @@ bool        XHTMLStream::isTagOpen(string const & stag)
 // best to make things work.
 XHTMLStream & XHTMLStream::operator<<(EndTag const & etag)
 {
+       if (etag.tag_.empty())
+               return *this;
        // first make sure we're not closing an empty tag
        if (!pending_tags_.empty()) {
                StartTag const & stag = pending_tags_.back();
@@ -254,25 +350,27 @@ XHTMLStream & XHTMLStream::operator<<(EndTag const & etag)
                for (; dit != den; ++dit) {
                        if (dit->tag_ == etag.tag_) {
                                // it was pending, so we just erase it
-                               LYXERR0("Tried to close pending tag `" << etag.tag_ 
-                                       << "' when other tags were pending. Tag discarded.");
+                               writeError("Tried to close pending tag `" + etag.tag_ 
+                                       + "' when other tags were pending. Last pending tag is `"
+                                       + pending_tags_.back().tag_ + "'. Tag discarded.");
                                pending_tags_.erase(dit);
                                return *this;
                        }
                }
                // so etag isn't itself pending. is it even open?
                if (!isTagOpen(etag.tag_)) {
-                       LYXERR0("Tried to close `" << etag.tag_ 
-                                << "' when tag was not open. Tag discarded.");
+                       writeError("Tried to close `" + etag.tag_ 
+                                + "' when tag was not open. Tag discarded.");
                        return *this;
                }
                // ok, so etag is open.
                // our strategy will be as below: we will do what we need to 
                // do to close this tag.
-               LYXERR0("Closing tag `" << etag.tag_ 
-                       << "' when other tags are pending. Discarded pending tags:");
+               string estr = "Closing tag `" + etag.tag_ 
+                       + "' when other tags are pending. Discarded pending tags:\n";
                for (dit = pending_tags_.begin(); dit != den; ++dit)
-                       LYXERR0(dit->tag_);
+                       estr += dit->tag_ + "\n";
+               writeError(estr);
                // clear the pending tags...
                pending_tags_.clear();
                // ...and then just fall through.
@@ -290,8 +388,8 @@ XHTMLStream & XHTMLStream::operator<<(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_)) {
-               LYXERR0("Tried to close `" << etag.tag_ 
-                       << "' when tag was not open. Tag discarded.");
+               writeError("Tried to close `" + etag.tag_ 
+                       + "' when tag was not open. Tag discarded.");
                return *this;
        }
        
@@ -308,8 +406,8 @@ XHTMLStream & XHTMLStream::operator<<(EndTag const & etag)
                                break;
                        if (!html::isFontTag(rit->tag_)) {
                                // we'll just leave it and, presumably, have to close it later.
-                               LYXERR0("Unable to close font tag `" << etag.tag_ 
-                                       << "' due to open non-font tag `" << rit->tag_ << "'.");
+                               writeError("Unable to close font tag `" + etag.tag_ 
+                                       + "' due to open non-font tag `" + rit->tag_ + "'.");
                                return *this;
                        }
                }
@@ -330,6 +428,8 @@ XHTMLStream & XHTMLStream::operator<<(EndTag const & etag)
                }
                // now close our tag...
                os_ << etag.asEndTag();
+               tag_stack_.pop_back();
+
                // ...and restore the other tags.
                rit = fontstack.rbegin();
                ren = fontstack.rend();
@@ -342,11 +442,11 @@ XHTMLStream & XHTMLStream::operator<<(EndTag const & etag)
        // so other tags were opened before this one and not properly closed. 
        // so we'll close them, too. that may cause other issues later, but it 
        // at least guarantees proper nesting.
-       LYXERR0("Closing tag `" << etag.tag_ 
-               << "' when other tags are open, namely:");
+       writeError("Closing tag `" + etag.tag_ 
+               + "' when other tags are open, namely:");
        StartTag curtag = tag_stack_.back();
        while (curtag.tag_ != etag.tag_) {
-               LYXERR0(curtag.tag_);
+               writeError(curtag.tag_);
                os_ << curtag.asEndTag();
                tag_stack_.pop_back();
                curtag = tag_stack_.back();
@@ -459,12 +559,12 @@ ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
                if (par != pbegin)
                        xs.cr();
 
-               // FIXME Should we really allow anything other than 'p' here?
-               
                // If we are already in a paragraph, and this is the first one, then we
                // do not want to open the paragraph tag.
-               bool const opened = 
-                       (par == pbegin && runparams.html_in_par) ? false : true;
+               // we also do not want to open it if the current layout does not permit
+               // multiple paragraphs.
+               bool const opened = runparams.html_make_pars &&
+                       (par != pbegin || !runparams.html_in_par);
                if (opened)
                        openTag(xs, lay);
                docstring const deferred = 
@@ -485,7 +585,7 @@ ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
                        xs.cr();
                }
                if (!deferred.empty()) {
-                       xs << deferred;
+                       xs << XHTMLStream::NextRaw() << deferred;
                        xs.cr();
                }
        }
@@ -514,7 +614,8 @@ ParagraphList::const_iterator makeBibliography(Buffer const & buf,
 
 bool isNormalEnv(Layout const & lay)
 {
-       return lay.latextype == LATEX_ENVIRONMENT;
+       return lay.latextype == LATEX_ENVIRONMENT
+           || lay.latextype == LATEX_BIB_ENVIRONMENT;
 }
 
        
@@ -541,8 +642,17 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
                Layout const & style = par->layout();
                // the counter only gets stepped if we're in some kind of list,
                // or if it's the first time through.
-               if (!style.counter.empty() && (par == pbegin || !isNormalEnv(style)))
-                       buf.params().documentClass().counters().step(style.counter);
+               // note that enum, etc, are handled automatically.
+               // 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();
+               docstring const & cntr = style.counter;
+               if (!style.counter.empty() 
+                   && (par == pbegin || !isNormalEnv(style)) 
+                               && cnts.hasCounter(cntr)
+               )
+                       cnts.step(cntr);
                ParagraphList::const_iterator send;
                // this will be positive, if we want to skip the initial word
                // (if it's been taken for the label).
@@ -561,7 +671,6 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
                                        closeItemTag(xs, *lastlay);
                                        lastlay = 0;
                                }
-                               bool const labelfirst = style.htmllabelfirst();
                                if (isNormalEnv(style)) {
                                        // in this case, we print the label only for the first 
                                        // paragraph (as in a theorem).
@@ -577,12 +686,13 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
                                                xs.cr();
                                        }
                                }       else { // some kind of list
+                                       bool const labelfirst = style.htmllabelfirst();
                                        if (!labelfirst)
                                                openItemTag(xs, style);
                                        if (style.labeltype == LABEL_MANUAL
                                            && style.htmllabeltag() != "NONE") {
                                                openLabelTag(xs, style);
-//                                             sep = par->firstWordLyXHTML(xs, runparams);
+                                               sep = par->firstWordLyXHTML(xs, runparams);
                                                closeLabelTag(xs, style);
                                                xs.cr();
                                        }
@@ -595,19 +705,15 @@ ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
                                        }
                                        if (labelfirst)
                                                openItemTag(xs, style);
-                                       else
-                                               xs << StartTag("span", "class='" + to_utf8(style.name()) + " inneritem'>");
                                }
                                par->simpleLyXHTMLOnePar(buf, xs, runparams, 
-                                       text.outerFont(distance(begin, par)), sep);
-                               if (!isNormalEnv(style) && !labelfirst)
-                                       xs << EndTag("span");
+                                       text.outerFont(distance(begin, par)), false, sep);
                                ++par;
                                // We may not want to close the tag yet, in particular,
                                // if we're not at the end...
                                if (par != pend 
                                        //  and are doing items...
-                                        && style.latextype == LATEX_ITEM_ENVIRONMENT
+                                        && !isNormalEnv(style)
                                         // and if the depth has changed...
                                         && par->params().depth() != origdepth) {
                                         // then we'll save this layout for later, and close it when