]> git.lyx.org Git - lyx.git/blobdiff - src/output_xhtml.cpp
prepare Qt 5.6 builds
[lyx.git] / src / output_xhtml.cpp
index 7212bb2de10651867be456f4f49dd4f111a09e44..e960e2c5d53ea0f8ba846e612b5c6dcd60cd26b0 100644 (file)
@@ -4,7 +4,7 @@
  * Licence details can be found in the file COPYING.
  *
  * \author Richard Heck
- * 
+ *
  * This code is based upon output_docbook.cpp
  *
  * Full author contact details are available in file CREDITS.
@@ -85,53 +85,10 @@ docstring htmlize(docstring const & str, XHTMLStream::EscapeSettings e)
 }
 
 
-string escapeChar(char c, XHTMLStream::EscapeSettings e)
-{
-       string str;
-       switch (e) {
-       case XHTMLStream::ESCAPE_NONE:
-               str += c;
-               break;
-       case XHTMLStream::ESCAPE_ALL:
-               if (c == '<') {
-                       str += "&lt;";
-                       break;
-               } else if (c == '>') {
-                       str += "&gt;";
-                       break;
-               }
-       // fall through
-       case XHTMLStream::ESCAPE_AND:
-               if (c == '&')
-                       str += "&amp;";
-               else
-                       str     +=c ;
-               break;
-       }
-       return str;
-}
-
-
-// escape what needs escaping
-string htmlize(string const & str, XHTMLStream::EscapeSettings e)
-{
-       ostringstream d;
-       string::const_iterator it = str.begin();
-       string::const_iterator en = str.end();
-       for (; it != en; ++it)
-               d << escapeChar(*it, e);
-       return d.str();
-}
-
-
-string cleanAttr(string const & str)
+docstring escapeChar(char c, XHTMLStream::EscapeSettings e)
 {
-       string newname;
-       string::const_iterator it = str.begin();
-       string::const_iterator en = str.end();
-       for (; it != en; ++it)
-               newname += isAlnumASCII(*it) ? *it : '_';
-       return newname; 
+       LATTEST(static_cast<unsigned char>(c) < 0x80);
+       return escapeChar(static_cast<char_type>(c), e);
 }
 
 
@@ -144,17 +101,17 @@ docstring cleanAttr(docstring const & str)
                char_type const c = *it;
                newname += isAlnumASCII(c) ? c : char_type('_');
        }
-       return newname; 
+       return newname;
 }
 
 
 docstring StartTag::writeTag() const
 {
-       string output = "<" + tag_;
+       docstring output = '<' + from_utf8(tag_);
        if (!attr_.empty())
-               output += " " + html::htmlize(attr_, XHTMLStream::ESCAPE_NONE);
+               output += ' ' + html::htmlize(from_utf8(attr_), XHTMLStream::ESCAPE_NONE);
        output += ">";
-       return from_utf8(output);
+       return output;
 }
 
 
@@ -193,11 +150,11 @@ docstring ParTag::writeTag() const
 
 docstring CompTag::writeTag() const
 {
-       string output = "<" + tag_;
+       docstring output = '<' + from_utf8(tag_);
        if (!attr_.empty())
-               output += " " + html::htmlize(attr_, XHTMLStream::ESCAPE_NONE);
+               output += ' ' + html::htmlize(from_utf8(attr_), XHTMLStream::ESCAPE_NONE);
        output += " />";
-       return from_utf8(output);
+       return output;
 }
 
 
@@ -205,7 +162,7 @@ docstring CompTag::writeTag() const
 namespace {
 
 string fontToTag(html::FontTypes type)
- {
+{
        switch(type) {
        case FT_EMPH:
                return "em";
@@ -221,57 +178,90 @@ string fontToTag(html::FontTypes type)
                return "del";
        case FT_ITALIC:
                return "i";
+       case FT_UPRIGHT:
        case FT_SLANTED:
        case FT_SMALLCAPS:
        case FT_ROMAN:
        case FT_SANS:
-       case FT_TYPER:
+       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);
+string fontToAttribute(html::FontTypes type)
+{
        switch(type) {
        case FT_EMPH:
-               return html::StartTag(tag);
        case FT_BOLD:
-               return html::StartTag(tag);
+               return "";
        case FT_NOUN:
-               return html::StartTag(tag, "class='lyxnoun'");
+               return "class='lyxnoun'";
        case FT_UBAR:
-               return html::StartTag(tag);
+               return "";
        case FT_DBAR:
-               return html::StartTag(tag, "class='dline'");
+               return "class='dline'";
        case FT_SOUT:
-               return html::StartTag(tag, "class='strikeout'");
+               return "class='strikeout'";
        case FT_WAVE:
-               return html::StartTag(tag, "class='wline'");
+               return "class='wline'";
        case FT_ITALIC:
-               return html::StartTag(tag);
+               return "";
+       case FT_UPRIGHT:
+               return "style='font-style:normal;'";
        case FT_SLANTED:
-               return html::StartTag(tag, "style='font-style:oblique;'");
+               return "style='font-style:oblique;'";
        case FT_SMALLCAPS:
-               return html::StartTag(tag, "style='font-variant:small-caps;'");
+               return "style='font-variant:small-caps;'";
        case FT_ROMAN:
-               return html::StartTag(tag, "style='font-family:serif;'");
+               return "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;'");
+               return "style='font-family:sans-serif;'";
+       case FT_TYPE:
+               return "style='font-family:monospace;'";
+       case FT_SIZE_TINY:
+       case FT_SIZE_SCRIPT:
+       case FT_SIZE_FOOTNOTE:
+               return "style='font-size:x-small;'";
+       case FT_SIZE_SMALL:
+               return "style='font-size:small;'";
+       case FT_SIZE_NORMAL:
+               return "style='font-size:normal;'";
+       case FT_SIZE_LARGE:
+               return "style='font-size:large;'";
+       case FT_SIZE_LARGER:
+       case FT_SIZE_LARGEST:
+               return "style='font-size:x-large;'";
+       case FT_SIZE_HUGE:
+       case FT_SIZE_HUGER:
+               return "style='font-size:xx-large;'";
+       case FT_SIZE_INCREASE:
+               return "style='font-size:larger;'";
+       case FT_SIZE_DECREASE:
+               return "style='font-size:smaller;'";
        }
        // kill warning
-       return StartTag("");
+       return "";
 }
 
 } // end anonymous namespace
 
 
 FontTag::FontTag(FontTypes type)
-  : StartTag(fontToStartTag(type)), font_type_(type)
+  : StartTag(fontToTag(type), fontToAttribute(type)), font_type_(type)
 {}
 
 
@@ -357,7 +347,7 @@ bool XHTMLStream::closeFontTags()
                LBUFERR(!tag_stack_.empty());
                curtag = tag_stack_.back();
        }
-       
+
        if (*curtag == parsep_tag)
                return true;
 
@@ -405,7 +395,7 @@ void XHTMLStream::endParagraph()
                return;
        }
 
-       // this case is also normal, if the parsep tag is the last one 
+       // 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()) {
                TagPtr const cur_tag = tag_stack_.back();
@@ -462,7 +452,7 @@ XHTMLStream & XHTMLStream::operator<<(char_type c)
 XHTMLStream & XHTMLStream::operator<<(char c)
 {
        clearTagDeque();
-       string const d = html::escapeChar(c, escape_);
+       os_ << html::escapeChar(c, escape_);
        escape_ = ESCAPE_ALL;
        return *this;
 }
@@ -478,13 +468,13 @@ XHTMLStream & XHTMLStream::operator<<(int i)
 
 
 XHTMLStream & XHTMLStream::operator<<(EscapeSettings e)
-{ 
+{
        escape_ = e;
        return *this;
 }
 
 
-XHTMLStream & XHTMLStream::operator<<(html::StartTag const & tag) 
+XHTMLStream & XHTMLStream::operator<<(html::StartTag const & tag)
 {
        if (tag.tag_.empty())
                return *this;
@@ -504,7 +494,7 @@ XHTMLStream & XHTMLStream::operator<<(html::ParTag const & tag)
 }
 
 
-XHTMLStream & XHTMLStream::operator<<(html::CompTag const & tag) 
+XHTMLStream & XHTMLStream::operator<<(html::CompTag const & tag)
 {
        if (tag.tag_.empty())
                return *this;
@@ -566,7 +556,7 @@ bool XHTMLStream::isTagPending(html::StartTag const & stag) const
 
 
 // this is complicated, because we want to make sure that
-// everything is properly nested. the code ought to make 
+// everything is properly nested. the code ought to make
 // sure of that, but we won't assert (yet) if we run into
 // a problem. we'll just output error messages and try our
 // best to make things work.
@@ -579,14 +569,14 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
        if (!pending_tags_.empty()) {
 
                if (etag == *pending_tags_.back()) {
-                       // we have <tag></tag>, so we discard it and remove it 
+                       // 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. 
+               // to close.
 
                // is this tag itself pending?
                // non-const iterators because we may call erase().
@@ -595,26 +585,27 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
                for (; dit != den; ++dit) {
                        if (etag == **dit) {
                                // it was pending, so we just erase it
-                               writeError("Tried to close pending tag `" + etag.tag_ 
+                               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)) {
-                       writeError("Tried to close `" + etag.tag_ 
+                       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 
+               // our strategy will be as below: we will do what we need to
                // do to close this tag.
-               string estr = "Closing tag `" + etag.tag_ 
+               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();
@@ -625,7 +616,7 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
        if (tag_stack_.empty()) {
                writeError("Tried to close `" + etag.tag_
                         + "' when no tags were open!");
-               return *this;           
+               return *this;
        }
 
        // is the tag we are closing the last one we opened?
@@ -635,16 +626,16 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
                // ...and forget about it
                tag_stack_.pop_back();
                return *this;
-       } 
-       
-       // we are trying to close a tag other than the one last opened. 
+       }
+
+       // 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)) {
-               writeError("Tried to close `" + etag.tag_ 
+               writeError("Tried to close `" + etag.tag_
                        + "' when tag was not open. Tag discarded.");
                return *this;
        }
-       
+
        // so the tag was opened, but other tags have been opened since
        // and not yet closed.
        // if it's a font tag, though...
@@ -658,12 +649,12 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
                                break;
                        if (!(*rit)->asFontTag()) {
                                // we'll just leave it and, presumably, have to close it later.
-                               writeError("Unable to close font tag `" + etag.tag_ 
+                               writeError("Unable to close font tag `" + etag.tag_
                                        + "' due to open non-font tag `" + (*rit)->tag_ + "'.");
                                return *this;
                        }
                }
-               
+
                // so we have e.g.:
                //    <em>this is <strong>bold
                // and are being asked to closed em. we want:
@@ -678,7 +669,7 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
                        tag_stack_.pop_back();
                        curtag = tag_stack_.back();
                }
-    os_ << etag.writeEndTag();
+               os_ << etag.writeEndTag();
                tag_stack_.pop_back();
 
                // ...and restore the other tags.
@@ -688,12 +679,12 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
                        pending_tags_.push_back(*rit);
                return *this;
        }
-       
+
        // it wasn't a font tag.
-       // 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 
+       // 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.
-       writeError("Closing tag `" + etag.tag_ 
+       writeError("Closing tag `" + etag.tag_
                + "' when other tags are open, namely:");
        TagPtr curtag = tag_stack_.back();
        while (etag != *curtag) {
@@ -706,35 +697,15 @@ XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
        // curtag is now the one we actually want.
        os_ << curtag->writeEndTag();
        tag_stack_.pop_back();
-       
+
        return *this;
 }
 
 // End code for XHTMLStream
 
 namespace {
-       
-// convenience functions
-
-inline void openTag(XHTMLStream & xs, Layout const & lay)
-{
-       xs << html::StartTag(lay.htmltag(), lay.htmlattr());
-}
-
-
-void openTag(XHTMLStream & xs, Layout const & lay, 
-             ParagraphParameters const & params)
-{
-       // FIXME Are there other things we should handle here?
-       string const align = alignmentToCSS(params.align());
-       if (align.empty()) {
-               openTag(xs, lay);
-               return;
-       }
-       string attrs = lay.htmlattr() + " style='text-align: " + align + ";'";
-       xs << html::StartTag(lay.htmltag(), attrs);
-}
 
+// convenience functions
 
 inline void openParTag(XHTMLStream & xs, Layout const & lay,
                        std::string parlabel)
@@ -782,7 +753,7 @@ inline void openItemTag(XHTMLStream & xs, Layout const & lay)
 }
 
 
-void openItemTag(XHTMLStream & xs, Layout const & lay, 
+void openItemTag(XHTMLStream & xs, Layout const & lay,
              ParagraphParameters const & params)
 {
        // FIXME Are there other things we should handle here?
@@ -815,7 +786,7 @@ ParagraphList::const_iterator findLastParagraph(
 
 
 ParagraphList::const_iterator findEndOfEnvironment(
-               ParagraphList::const_iterator const pstart,
+               ParagraphList::const_iterator const pstart,
                ParagraphList::const_iterator const & pend)
 {
        ParagraphList::const_iterator p = pstart;
@@ -828,15 +799,19 @@ ParagraphList::const_iterator findEndOfEnvironment(
                // 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;
@@ -874,17 +849,17 @@ ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
                        openParTag(xs, lay, par->params(),
                                   make_parid ? par->magicLabel() : "");
 
-               docstring const deferred = 
+               docstring const deferred =
                        par->simpleLyXHTMLOnePar(buf, xs, runparams, text.outerFont(distance(begin, par)));
 
                // We want to issue the closing tag if either:
                //   (i)  We opened it, and either html_in_par is false,
                //        or we're not in the last paragraph, anyway.
-               //   (ii) We didn't open it and html_in_par is true, 
+               //   (ii) We didn't open it and html_in_par is true,
                //        but we are in the first par, and there is a next par.
                ParagraphList::const_iterator nextpar = par;
                ++nextpar;
-               bool const needclose = 
+               bool const needclose =
                        (opened && (!runparams.html_in_par || nextpar != pend))
                        || (!opened && runparams.html_in_par && par == pbegin && nextpar != pend);
                if (needclose) {
@@ -904,7 +879,7 @@ ParagraphList::const_iterator makeBibliography(Buffer const & buf,
                                OutputParams const & runparams,
                                Text const & text,
                                ParagraphList::const_iterator const & pbegin,
-                               ParagraphList::const_iterator const & pend) 
+                               ParagraphList::const_iterator const & pend)
 {
        // FIXME XHTML
        // Use TextClass::htmlTOCLayout() to figure out how we should look.
@@ -926,13 +901,13 @@ bool isNormalEnv(Layout const & lay)
            || lay.latextype == LATEX_BIB_ENVIRONMENT;
 }
 
-       
+
 ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
                                              XHTMLStream & xs,
                                              OutputParams const & runparams,
                                              Text const & text,
                                              ParagraphList::const_iterator const & pbegin,
-                                             ParagraphList::const_iterator const & pend) 
+                                             ParagraphList::const_iterator const & pend)
 {
        ParagraphList::const_iterator const begin = text.paragraphs().begin();
        ParagraphList::const_iterator par = pbegin;
@@ -956,22 +931,19 @@ ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
                // "ii", etc, as with enum.
                Counters & cnts = buf.masterBuffer()->params().documentClass().counters();
                docstring const & cntr = style.counter;
-               if (!style.counter.empty() 
-                   && (par == pbegin || !isNormalEnv(style)) 
+               if (!style.counter.empty()
+                   && (par == pbegin || !isNormalEnv(style))
                                && cnts.hasCounter(cntr)
                )
                        cnts.step(cntr, OutputUpdate);
                ParagraphList::const_iterator send;
-               // this will be positive, if we want to skip the initial word
-               // (if it's been taken for the label).
-               pos_type sep = 0;
 
                switch (style.latextype) {
                case LATEX_ENVIRONMENT:
                case LATEX_LIST_ENVIRONMENT:
                case LATEX_ITEM_ENVIRONMENT: {
-                       // There are two possiblities in this case. 
-                       // One is that we are still in the environment in which we 
+                       // There are two possiblities in this case.
+                       // 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) {
                                LATTEST(bstyle == style);
@@ -979,19 +951,22 @@ ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
                                        closeItemTag(xs, *lastlay);
                                        lastlay = 0;
                                }
-                               
+
+                               // this will be positive, if we want to skip the
+                               // initial word (if it's been taken for the label).
+                               pos_type sep = 0;
                                bool const labelfirst = style.htmllabelfirst();
                                if (!labelfirst)
                                        openItemTag(xs, style, par->params());
-                               
+
                                // label output
-                               if (style.labeltype != LABEL_NO_LABEL && 
+                               if (style.labeltype != LABEL_NO_LABEL &&
                                    style.htmllabeltag() != "NONE") {
                                        if (isNormalEnv(style)) {
-                                               // in this case, we print the label only for the first 
+                                               // in this case, we print the label only for the first
                                                // paragraph (as in a theorem).
                                                if (par == pbegin) {
-                                                       docstring const lbl = 
+                                                       docstring const lbl =
                                                                        pbegin->params().labelString();
                                                        if (!lbl.empty()) {
                                                                openLabelTag(xs, style);
@@ -1000,7 +975,7 @@ ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
                                                        }
                                                        xs << html::CR();
                                                }
-                                       }       else { // some kind of list
+                                       } else { // some kind of list
                                                if (style.labeltype == LABEL_MANUAL) {
                                                        openLabelTag(xs, style);
                                                        sep = par->firstWordLyXHTML(xs, runparams);
@@ -1019,13 +994,13 @@ ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
                                if (labelfirst)
                                        openItemTag(xs, style, par->params());
 
-                               par->simpleLyXHTMLOnePar(buf, xs, runparams, 
+                               par->simpleLyXHTMLOnePar(buf, xs, runparams,
                                        text.outerFont(distance(begin, par)), sep);
                                ++par;
 
                                // We may not want to close the tag yet, in particular:
                                // If we're not at the end...
-                               if (par != pend 
+                               if (par != pend
                                        //  and are doing items...
                                         && !isNormalEnv(style)
                                         // and if the depth has changed...
@@ -1147,7 +1122,7 @@ void xhtmlParagraphs(Text const & text,
                case LATEX_COMMAND: {
                        // The files with which we are working never have more than
                        // one paragraph in a command structure.
-                       // FIXME 
+                       // FIXME
                        // if (ourparams.html_in_par)
                        //   fix it so we don't get sections inside standard, e.g.
                        // note that we may then need to make runparams not const, so we