2 * \file output_xhtml.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * This code is based upon output_docbook.cpp
10 * Full author contact details are available in file CREDITS.
15 #include "output_xhtml.h"
18 #include "buffer_funcs.h"
19 #include "BufferParams.h"
23 #include "OutputParams.h"
24 #include "Paragraph.h"
25 #include "ParagraphList.h"
26 #include "ParagraphParameters.h"
29 #include "TextClass.h"
31 #include "support/convert.h"
32 #include "support/debug.h"
33 #include "support/lassert.h"
34 #include "support/lstrings.h"
35 #include "support/textutils.h"
39 // Uncomment to activate debugging code.
40 // #define XHTML_DEBUG
43 using namespace lyx::support;
49 docstring escapeChar(char_type c, XHTMLStream::EscapeSettings e)
53 case XHTMLStream::ESCAPE_NONE:
56 case XHTMLStream::ESCAPE_ALL:
60 } else if (c == '>') {
65 case XHTMLStream::ESCAPE_AND:
76 // escape what needs escaping
77 docstring htmlize(docstring const & str, XHTMLStream::EscapeSettings e)
80 docstring::const_iterator it = str.begin();
81 docstring::const_iterator en = str.end();
82 for (; it != en; ++it)
83 d << escapeChar(*it, e);
88 string escapeChar(char c, XHTMLStream::EscapeSettings e)
92 case XHTMLStream::ESCAPE_NONE:
95 case XHTMLStream::ESCAPE_ALL:
99 } else if (c == '>') {
104 case XHTMLStream::ESCAPE_AND:
115 // escape what needs escaping
116 string htmlize(string const & str, XHTMLStream::EscapeSettings e)
119 string::const_iterator it = str.begin();
120 string::const_iterator en = str.end();
121 for (; it != en; ++it)
122 d << escapeChar(*it, e);
127 string cleanAttr(string const & str)
130 string::const_iterator it = str.begin();
131 string::const_iterator en = str.end();
132 for (; it != en; ++it)
133 newname += isAlnumASCII(*it) ? *it : '_';
138 docstring cleanAttr(docstring const & str)
141 docstring::const_iterator it = str.begin();
142 docstring::const_iterator en = str.end();
143 for (; it != en; ++it) {
144 char_type const c = *it;
145 newname += isAlnumASCII(c) ? c : char_type('_');
151 docstring StartTag::writeTag() const
153 string output = "<" + tag_;
155 output += " " + html::htmlize(attr_, XHTMLStream::ESCAPE_NONE);
157 return from_utf8(output);
161 docstring StartTag::writeEndTag() const
163 string output = "</" + tag_ + ">";
164 return from_utf8(output);
168 bool StartTag::operator==(FontTag const & rhs) const
174 docstring EndTag::writeEndTag() const
176 string output = "</" + tag_ + ">";
177 return from_utf8(output);
181 docstring ParTag::writeTag() const
183 docstring output = StartTag::writeTag();
188 string const pattr = "id='" + parid_ + "'";
189 output += html::CompTag("a", pattr).writeTag();
194 docstring CompTag::writeTag() const
196 string output = "<" + tag_;
198 output += " " + html::htmlize(attr_, XHTMLStream::ESCAPE_NONE);
200 return from_utf8(output);
207 string fontToTag(html::FontTypes type)
232 case FT_SIZE_FOOTNOTE:
237 case FT_SIZE_LARGEST:
240 case FT_SIZE_INCREASE:
241 case FT_SIZE_DECREASE:
248 string fontToAttribute(html::FontTypes type)
255 return "class='lyxnoun'";
259 return "class='dline'";
261 return "class='strikeout'";
263 return "class='wline'";
267 return "style='font-style:normal;'";
269 return "style='font-style:oblique;'";
271 return "style='font-variant:small-caps;'";
273 return "style='font-family:serif;'";
275 return "style='font-family:sans-serif;'";
277 return "style='font-family:monospace;'";
280 case FT_SIZE_FOOTNOTE:
281 return "style='font-size:x-small;'";
283 return "style='font-size:small;'";
285 return "style='font-size:normal;'";
287 return "style='font-size:large;'";
289 case FT_SIZE_LARGEST:
290 return "style='font-size:x-large;'";
293 return "style='font-size:xx-large;'";
294 case FT_SIZE_INCREASE:
295 return "style='font-size:larger;'";
296 case FT_SIZE_DECREASE:
297 return "style='font-size:smaller;'";
303 } // end anonymous namespace
306 FontTag::FontTag(FontTypes type)
307 : StartTag(fontToTag(type), fontToAttribute(type)), font_type_(type)
311 bool FontTag::operator==(StartTag const & tag) const
313 FontTag const * const ftag = tag.asFontTag();
316 return (font_type_ == ftag->font_type_);
320 EndFontTag::EndFontTag(FontTypes type)
321 : EndTag(fontToTag(type)), font_type_(type)
328 ////////////////////////////////////////////////////////////////
332 ////////////////////////////////////////////////////////////////
334 XHTMLStream::XHTMLStream(odocstream & os)
335 : os_(os), escape_(ESCAPE_ALL)
340 void XHTMLStream::dumpTagStack(string const & msg) const
342 writeError(msg + ": Tag Stack");
343 TagStack::const_reverse_iterator it = tag_stack_.rbegin();
344 TagStack::const_reverse_iterator en = tag_stack_.rend();
345 for (; it != en; ++it) {
346 writeError(it->tag_);
348 writeError("Pending Tags");
349 it = pending_tags_.rbegin();
350 en = pending_tags_.rend();
351 for (; it != en; ++it) {
352 writeError(it->tag_);
354 writeError("End Tag Stack");
359 void XHTMLStream::writeError(std::string const & s) const
362 os_ << from_utf8("<!-- Output Error: " + s + " -->\n");
367 // an illegal tag for internal use
368 static html::StartTag const parsep_tag("&LyX_parsep_tag&");
372 bool XHTMLStream::closeFontTags()
374 if (isTagPending(parsep_tag))
375 // we haven't had any content
378 // this may be a useless check, since we ought at least to have
379 // the parsep_tag. but it can't hurt too much to be careful.
380 if (tag_stack_.empty())
383 // first, we close any open font tags we can close
384 TagPtr curtag = tag_stack_.back();
385 while (curtag->asFontTag()) {
386 os_ << curtag->writeEndTag();
387 tag_stack_.pop_back();
388 // this shouldn't happen, since then the font tags
389 // weren't in any other tag.
390 LBUFERR(!tag_stack_.empty());
391 curtag = tag_stack_.back();
394 if (*curtag == parsep_tag)
397 // so we've hit a non-font tag.
398 writeError("Tags still open in closeFontTags(). Probably not a problem,\n"
399 "but you might want to check these tags:");
400 TagDeque::const_reverse_iterator it = tag_stack_.rbegin();
401 TagDeque::const_reverse_iterator const en = tag_stack_.rend();
402 for (; it != en; ++it) {
403 if (**it == parsep_tag)
405 writeError((*it)->tag_);
411 void XHTMLStream::startParagraph(bool keep_empty)
413 pending_tags_.push_back(makeTagPtr(html::StartTag(parsep_tag)));
419 void XHTMLStream::endParagraph()
421 if (isTagPending(parsep_tag)) {
422 // this case is normal. it just means we didn't have content,
423 // so the parsep_tag never got moved onto the tag stack.
424 while (!pending_tags_.empty()) {
425 // clear all pending tags up to and including the parsep tag.
426 // note that we work from the back, because we want to get rid
427 // of everything that hasn't been used.
428 TagPtr const cur_tag = pending_tags_.back();
429 pending_tags_.pop_back();
430 if (*cur_tag == parsep_tag)
436 if (!isTagOpen(parsep_tag)) {
437 writeError("No paragraph separation tag found in endParagraph().");
441 // this case is also normal, if the parsep tag is the last one
442 // on the stack. otherwise, it's an error.
443 while (!tag_stack_.empty()) {
444 TagPtr const cur_tag = tag_stack_.back();
445 tag_stack_.pop_back();
446 if (*cur_tag == parsep_tag)
448 writeError("Tag `" + cur_tag->tag_ + "' still open at end of paragraph. Closing.");
449 os_ << cur_tag->writeEndTag();
454 void XHTMLStream::clearTagDeque()
456 while (!pending_tags_.empty()) {
457 TagPtr const tag = pending_tags_.front();
458 if (*tag != parsep_tag)
460 os_ << tag->writeTag();
461 tag_stack_.push_back(tag);
462 pending_tags_.pop_front();
467 XHTMLStream & XHTMLStream::operator<<(docstring const & d)
470 os_ << html::htmlize(d, escape_);
471 escape_ = ESCAPE_ALL;
476 XHTMLStream & XHTMLStream::operator<<(const char * s)
479 docstring const d = from_ascii(s);
480 os_ << html::htmlize(d, escape_);
481 escape_ = ESCAPE_ALL;
486 XHTMLStream & XHTMLStream::operator<<(char_type c)
489 os_ << html::escapeChar(c, escape_);
490 escape_ = ESCAPE_ALL;
495 XHTMLStream & XHTMLStream::operator<<(char c)
498 os_ << html::escapeChar(c, escape_);
499 escape_ = ESCAPE_ALL;
504 XHTMLStream & XHTMLStream::operator<<(int i)
508 escape_ = ESCAPE_ALL;
513 XHTMLStream & XHTMLStream::operator<<(EscapeSettings e)
520 XHTMLStream & XHTMLStream::operator<<(html::StartTag const & tag)
522 if (tag.tag_.empty())
524 pending_tags_.push_back(makeTagPtr(tag));
531 XHTMLStream & XHTMLStream::operator<<(html::ParTag const & tag)
533 if (tag.tag_.empty())
535 pending_tags_.push_back(makeTagPtr(tag));
540 XHTMLStream & XHTMLStream::operator<<(html::CompTag const & tag)
542 if (tag.tag_.empty())
545 os_ << tag.writeTag();
551 XHTMLStream & XHTMLStream::operator<<(html::FontTag const & tag)
553 if (tag.tag_.empty())
555 pending_tags_.push_back(makeTagPtr(tag));
560 XHTMLStream & XHTMLStream::operator<<(html::CR const &)
563 os_ << from_ascii("\n");
568 bool XHTMLStream::isTagOpen(html::StartTag const & stag) const
570 TagDeque::const_iterator sit = tag_stack_.begin();
571 TagDeque::const_iterator const sen = tag_stack_.end();
572 for (; sit != sen; ++sit)
579 bool XHTMLStream::isTagOpen(html::EndTag const & etag) const
581 TagDeque::const_iterator sit = tag_stack_.begin();
582 TagDeque::const_iterator const sen = tag_stack_.end();
583 for (; sit != sen; ++sit)
590 bool XHTMLStream::isTagPending(html::StartTag const & stag) const
592 TagDeque::const_iterator sit = pending_tags_.begin();
593 TagDeque::const_iterator const sen = pending_tags_.end();
594 for (; sit != sen; ++sit)
601 // this is complicated, because we want to make sure that
602 // everything is properly nested. the code ought to make
603 // sure of that, but we won't assert (yet) if we run into
604 // a problem. we'll just output error messages and try our
605 // best to make things work.
606 XHTMLStream & XHTMLStream::operator<<(html::EndTag const & etag)
608 if (etag.tag_.empty())
611 // if this tag is pending, we can simply discard it.
612 if (!pending_tags_.empty()) {
614 if (etag == *pending_tags_.back()) {
615 // we have <tag></tag>, so we discard it and remove it
616 // from the pending_tags_.
617 pending_tags_.pop_back();
621 // there is a pending tag that isn't the one we are trying
624 // is this tag itself pending?
625 // non-const iterators because we may call erase().
626 TagDeque::iterator dit = pending_tags_.begin();
627 TagDeque::iterator const den = pending_tags_.end();
628 for (; dit != den; ++dit) {
630 // it was pending, so we just erase it
631 writeError("Tried to close pending tag `" + etag.tag_
632 + "' when other tags were pending. Last pending tag is `"
633 + to_utf8(pending_tags_.back()->writeTag())
634 + "'. Tag discarded.");
635 pending_tags_.erase(dit);
639 // so etag isn't itself pending. is it even open?
640 if (!isTagOpen(etag)) {
641 writeError("Tried to close `" + etag.tag_
642 + "' when tag was not open. Tag discarded.");
645 // ok, so etag is open.
646 // our strategy will be as below: we will do what we need to
647 // do to close this tag.
648 string estr = "Closing tag `" + etag.tag_
649 + "' when other tags are pending. Discarded pending tags:\n";
650 for (dit = pending_tags_.begin(); dit != den; ++dit)
651 estr += to_utf8(html::htmlize((*dit)->writeTag(), XHTMLStream::ESCAPE_ALL)) + "\n";
653 // clear the pending tags...
654 pending_tags_.clear();
655 // ...and then just fall through.
658 // make sure there are tags to be closed
659 if (tag_stack_.empty()) {
660 writeError("Tried to close `" + etag.tag_
661 + "' when no tags were open!");
665 // is the tag we are closing the last one we opened?
666 if (etag == *tag_stack_.back()) {
668 os_ << etag.writeEndTag();
669 // ...and forget about it
670 tag_stack_.pop_back();
674 // we are trying to close a tag other than the one last opened.
675 // let's first see if this particular tag is still open somehow.
676 if (!isTagOpen(etag)) {
677 writeError("Tried to close `" + etag.tag_
678 + "' when tag was not open. Tag discarded.");
682 // so the tag was opened, but other tags have been opened since
683 // and not yet closed.
684 // if it's a font tag, though...
685 if (etag.asFontTag()) {
686 // it won't be a problem if the other tags open since this one
687 // are also font tags.
688 TagDeque::const_reverse_iterator rit = tag_stack_.rbegin();
689 TagDeque::const_reverse_iterator ren = tag_stack_.rend();
690 for (; rit != ren; ++rit) {
693 if (!(*rit)->asFontTag()) {
694 // we'll just leave it and, presumably, have to close it later.
695 writeError("Unable to close font tag `" + etag.tag_
696 + "' due to open non-font tag `" + (*rit)->tag_ + "'.");
702 // <em>this is <strong>bold
703 // and are being asked to closed em. we want:
704 // <em>this is <strong>bold</strong></em><strong>
705 // first, we close the intervening tags...
706 TagPtr curtag = tag_stack_.back();
707 // ...remembering them in a stack.
709 while (etag != *curtag) {
710 os_ << curtag->writeEndTag();
711 fontstack.push_back(curtag);
712 tag_stack_.pop_back();
713 curtag = tag_stack_.back();
715 os_ << etag.writeEndTag();
716 tag_stack_.pop_back();
718 // ...and restore the other tags.
719 rit = fontstack.rbegin();
720 ren = fontstack.rend();
721 for (; rit != ren; ++rit)
722 pending_tags_.push_back(*rit);
726 // it wasn't a font tag.
727 // so other tags were opened before this one and not properly closed.
728 // so we'll close them, too. that may cause other issues later, but it
729 // at least guarantees proper nesting.
730 writeError("Closing tag `" + etag.tag_
731 + "' when other tags are open, namely:");
732 TagPtr curtag = tag_stack_.back();
733 while (etag != *curtag) {
734 writeError(curtag->tag_);
735 if (*curtag != parsep_tag)
736 os_ << curtag->writeEndTag();
737 tag_stack_.pop_back();
738 curtag = tag_stack_.back();
740 // curtag is now the one we actually want.
741 os_ << curtag->writeEndTag();
742 tag_stack_.pop_back();
747 // End code for XHTMLStream
751 // convenience functions
753 inline void openParTag(XHTMLStream & xs, Layout const & lay,
754 std::string parlabel)
756 xs << html::ParTag(lay.htmltag(), lay.htmlattr(), parlabel);
760 void openParTag(XHTMLStream & xs, Layout const & lay,
761 ParagraphParameters const & params,
762 std::string parlabel)
764 // FIXME Are there other things we should handle here?
765 string const align = alignmentToCSS(params.align());
767 openParTag(xs, lay, parlabel);
770 string attrs = lay.htmlattr() + " style='text-align: " + align + ";'";
771 xs << html::ParTag(lay.htmltag(), attrs, parlabel);
775 inline void closeTag(XHTMLStream & xs, Layout const & lay)
777 xs << html::EndTag(lay.htmltag());
781 inline void openLabelTag(XHTMLStream & xs, Layout const & lay)
783 xs << html::StartTag(lay.htmllabeltag(), lay.htmllabelattr());
787 inline void closeLabelTag(XHTMLStream & xs, Layout const & lay)
789 xs << html::EndTag(lay.htmllabeltag());
793 inline void openItemTag(XHTMLStream & xs, Layout const & lay)
795 xs << html::StartTag(lay.htmlitemtag(), lay.htmlitemattr(), true);
799 void openItemTag(XHTMLStream & xs, Layout const & lay,
800 ParagraphParameters const & params)
802 // FIXME Are there other things we should handle here?
803 string const align = alignmentToCSS(params.align());
805 openItemTag(xs, lay);
808 string attrs = lay.htmlattr() + " style='text-align: " + align + ";'";
809 xs << html::StartTag(lay.htmlitemtag(), attrs);
813 inline void closeItemTag(XHTMLStream & xs, Layout const & lay)
815 xs << html::EndTag(lay.htmlitemtag());
818 // end of convenience functions
820 ParagraphList::const_iterator findLastParagraph(
821 ParagraphList::const_iterator p,
822 ParagraphList::const_iterator const & pend)
824 for (++p; p != pend && p->layout().latextype == LATEX_PARAGRAPH; ++p)
831 ParagraphList::const_iterator findEndOfEnvironment(
832 ParagraphList::const_iterator const & pstart,
833 ParagraphList::const_iterator const & pend)
835 ParagraphList::const_iterator p = pstart;
836 Layout const & bstyle = p->layout();
837 size_t const depth = p->params().depth();
838 for (++p; p != pend; ++p) {
839 Layout const & style = p->layout();
840 // It shouldn't happen that e.g. a section command occurs inside
841 // a quotation environment, at a higher depth, but as of 6/2009,
842 // it can happen. We pretend that it's just at lowest depth.
843 if (style.latextype == LATEX_COMMAND)
846 // If depth is down, we're done
847 if (p->params().depth() < depth)
850 // If depth is up, we're not done
851 if (p->params().depth() > depth)
854 // FIXME I am not sure about the first check.
855 // Surely we *could* have different layouts that count as
856 // LATEX_PARAGRAPH, right?
857 if (style.latextype == LATEX_PARAGRAPH || style != bstyle)
864 ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
866 OutputParams const & runparams,
868 ParagraphList::const_iterator const & pbegin,
869 ParagraphList::const_iterator const & pend)
871 ParagraphList::const_iterator const begin = text.paragraphs().begin();
872 ParagraphList::const_iterator par = pbegin;
873 for (; par != pend; ++par) {
874 Layout const & lay = par->layout();
875 if (!lay.counter.empty())
876 buf.masterBuffer()->params().
877 documentClass().counters().step(lay.counter, OutputUpdate);
878 // FIXME We should see if there's a label to be output and
879 // do something with it.
883 // If we are already in a paragraph, and this is the first one, then we
884 // do not want to open the paragraph tag.
885 // we also do not want to open it if the current layout does not permit
886 // multiple paragraphs.
887 bool const opened = runparams.html_make_pars &&
888 (par != pbegin || !runparams.html_in_par);
889 bool const make_parid = !runparams.for_toc && runparams.html_make_pars;
892 openParTag(xs, lay, par->params(),
893 make_parid ? par->magicLabel() : "");
895 docstring const deferred =
896 par->simpleLyXHTMLOnePar(buf, xs, runparams, text.outerFont(distance(begin, par)));
898 // We want to issue the closing tag if either:
899 // (i) We opened it, and either html_in_par is false,
900 // or we're not in the last paragraph, anyway.
901 // (ii) We didn't open it and html_in_par is true,
902 // but we are in the first par, and there is a next par.
903 ParagraphList::const_iterator nextpar = par;
905 bool const needclose =
906 (opened && (!runparams.html_in_par || nextpar != pend))
907 || (!opened && runparams.html_in_par && par == pbegin && nextpar != pend);
912 if (!deferred.empty()) {
913 xs << XHTMLStream::ESCAPE_NONE << deferred << html::CR();
920 ParagraphList::const_iterator makeBibliography(Buffer const & buf,
922 OutputParams const & runparams,
924 ParagraphList::const_iterator const & pbegin,
925 ParagraphList::const_iterator const & pend)
928 // Use TextClass::htmlTOCLayout() to figure out how we should look.
929 xs << html::StartTag("h2", "class='bibliography'")
930 << pbegin->layout().labelstring(false)
931 << html::EndTag("h2")
933 << html::StartTag("div", "class='bibliography'")
935 makeParagraphs(buf, xs, runparams, text, pbegin, pend);
936 xs << html::EndTag("div");
941 bool isNormalEnv(Layout const & lay)
943 return lay.latextype == LATEX_ENVIRONMENT
944 || lay.latextype == LATEX_BIB_ENVIRONMENT;
948 ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
950 OutputParams const & runparams,
952 ParagraphList::const_iterator const & pbegin,
953 ParagraphList::const_iterator const & pend)
955 ParagraphList::const_iterator const begin = text.paragraphs().begin();
956 ParagraphList::const_iterator par = pbegin;
957 Layout const & bstyle = par->layout();
958 depth_type const origdepth = pbegin->params().depth();
960 // open tag for this environment
961 openParTag(xs, bstyle, pbegin->magicLabel());
964 // we will on occasion need to remember a layout from before.
965 Layout const * lastlay = 0;
967 while (par != pend) {
968 Layout const & style = par->layout();
969 // the counter only gets stepped if we're in some kind of list,
970 // or if it's the first time through.
971 // note that enum, etc, are handled automatically.
972 // FIXME There may be a bug here about user defined enumeration
973 // types. If so, then we'll need to take the counter and add "i",
974 // "ii", etc, as with enum.
975 Counters & cnts = buf.masterBuffer()->params().documentClass().counters();
976 docstring const & cntr = style.counter;
977 if (!style.counter.empty()
978 && (par == pbegin || !isNormalEnv(style))
979 && cnts.hasCounter(cntr)
981 cnts.step(cntr, OutputUpdate);
982 ParagraphList::const_iterator send;
984 switch (style.latextype) {
985 case LATEX_ENVIRONMENT:
986 case LATEX_LIST_ENVIRONMENT:
987 case LATEX_ITEM_ENVIRONMENT: {
988 // There are two possiblities in this case.
989 // One is that we are still in the environment in which we
990 // started---which we will be if the depth is the same.
991 if (par->params().depth() == origdepth) {
992 LATTEST(bstyle == style);
994 closeItemTag(xs, *lastlay);
998 // this will be positive, if we want to skip the
999 // initial word (if it's been taken for the label).
1001 bool const labelfirst = style.htmllabelfirst();
1003 openItemTag(xs, style, par->params());
1006 if (style.labeltype != LABEL_NO_LABEL &&
1007 style.htmllabeltag() != "NONE") {
1008 if (isNormalEnv(style)) {
1009 // in this case, we print the label only for the first
1010 // paragraph (as in a theorem).
1011 if (par == pbegin) {
1012 docstring const lbl =
1013 pbegin->params().labelString();
1015 openLabelTag(xs, style);
1017 closeLabelTag(xs, style);
1021 } else { // some kind of list
1022 if (style.labeltype == LABEL_MANUAL) {
1023 openLabelTag(xs, style);
1024 sep = par->firstWordLyXHTML(xs, runparams);
1025 closeLabelTag(xs, style);
1029 openLabelTag(xs, style);
1030 xs << par->params().labelString();
1031 closeLabelTag(xs, style);
1035 } // end label output
1038 openItemTag(xs, style, par->params());
1040 par->simpleLyXHTMLOnePar(buf, xs, runparams,
1041 text.outerFont(distance(begin, par)), sep);
1044 // We may not want to close the tag yet, in particular:
1045 // If we're not at the end...
1047 // and are doing items...
1048 && !isNormalEnv(style)
1049 // and if the depth has changed...
1050 && par->params().depth() != origdepth) {
1051 // then we'll save this layout for later, and close it when
1052 // we get another item.
1055 closeItemTag(xs, style);
1058 // The other possibility is that the depth has increased, in which
1059 // case we need to recurse.
1061 send = findEndOfEnvironment(par, pend);
1062 par = makeEnvironment(buf, xs, runparams, text, par, send);
1066 case LATEX_PARAGRAPH:
1067 send = findLastParagraph(par, pend);
1068 par = makeParagraphs(buf, xs, runparams, text, par, send);
1071 case LATEX_BIB_ENVIRONMENT:
1074 par = makeParagraphs(buf, xs, runparams, text, par, send);
1084 closeItemTag(xs, *lastlay);
1085 closeTag(xs, bstyle);
1091 void makeCommand(Buffer const & buf,
1093 OutputParams const & runparams,
1095 ParagraphList::const_iterator const & pbegin)
1097 Layout const & style = pbegin->layout();
1098 if (!style.counter.empty())
1099 buf.masterBuffer()->params().
1100 documentClass().counters().step(style.counter, OutputUpdate);
1102 bool const make_parid = !runparams.for_toc && runparams.html_make_pars;
1104 openParTag(xs, style, pbegin->params(),
1105 make_parid ? pbegin->magicLabel() : "");
1107 // Label around sectioning number:
1108 // FIXME Probably need to account for LABEL_MANUAL
1109 // FIXME Probably also need now to account for labels ABOVE and CENTERED.
1110 if (style.labeltype != LABEL_NO_LABEL) {
1111 openLabelTag(xs, style);
1112 xs << pbegin->params().labelString();
1113 closeLabelTag(xs, style);
1114 // Otherwise the label might run together with the text
1115 xs << from_ascii(" ");
1118 ParagraphList::const_iterator const begin = text.paragraphs().begin();
1119 pbegin->simpleLyXHTMLOnePar(buf, xs, runparams,
1120 text.outerFont(distance(begin, pbegin)));
1121 closeTag(xs, style);
1125 } // end anonymous namespace
1128 void xhtmlParagraphs(Text const & text,
1131 OutputParams const & runparams)
1133 ParagraphList const & paragraphs = text.paragraphs();
1134 if (runparams.par_begin == runparams.par_end) {
1135 runparams.par_begin = 0;
1136 runparams.par_end = paragraphs.size();
1138 pit_type bpit = runparams.par_begin;
1139 pit_type const epit = runparams.par_end;
1140 LASSERT(bpit < epit,
1141 { xs << XHTMLStream::ESCAPE_NONE << "<!-- XHTML output error! -->\n"; return; });
1143 OutputParams ourparams = runparams;
1144 ParagraphList::const_iterator const pend =
1145 (epit == (int) paragraphs.size()) ?
1146 paragraphs.end() : paragraphs.constIterator(epit);
1147 while (bpit < epit) {
1148 ParagraphList::const_iterator par = paragraphs.constIterator(bpit);
1149 if (par->params().startOfAppendix()) {
1150 // We want to reset the counter corresponding to toplevel sectioning
1151 Layout const & lay =
1152 buf.masterBuffer()->params().documentClass().getTOCLayout();
1153 docstring const cnt = lay.counter;
1156 buf.masterBuffer()->params().documentClass().counters();
1160 Layout const & style = par->layout();
1161 ParagraphList::const_iterator const lastpar = par;
1162 ParagraphList::const_iterator send;
1164 switch (style.latextype) {
1165 case LATEX_COMMAND: {
1166 // The files with which we are working never have more than
1167 // one paragraph in a command structure.
1169 // if (ourparams.html_in_par)
1170 // fix it so we don't get sections inside standard, e.g.
1171 // note that we may then need to make runparams not const, so we
1172 // can communicate that back.
1173 // FIXME Maybe this fix should be in the routines themselves, in case
1174 // they are called from elsewhere.
1175 makeCommand(buf, xs, ourparams, text, par);
1179 case LATEX_ENVIRONMENT:
1180 case LATEX_LIST_ENVIRONMENT:
1181 case LATEX_ITEM_ENVIRONMENT: {
1182 // FIXME Same fix here.
1183 send = findEndOfEnvironment(par, pend);
1184 par = makeEnvironment(buf, xs, ourparams, text, par, send);
1187 case LATEX_BIB_ENVIRONMENT: {
1188 // FIXME Same fix here.
1189 send = findEndOfEnvironment(par, pend);
1190 par = makeBibliography(buf, xs, ourparams, text, par, send);
1193 case LATEX_PARAGRAPH:
1194 send = findLastParagraph(par, pend);
1195 par = makeParagraphs(buf, xs, ourparams, text, par, send);
1198 bpit += distance(lastpar, par);
1203 string alignmentToCSS(LyXAlignment align)
1206 case LYX_ALIGN_BLOCK:
1207 // we are NOT going to use text-align: justify!!
1208 case LYX_ALIGN_LEFT:
1210 case LYX_ALIGN_RIGHT:
1212 case LYX_ALIGN_CENTER: