]> git.lyx.org Git - lyx.git/blob - src/output_xhtml.cpp
Comment.
[lyx.git] / src / output_xhtml.cpp
1 /**
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.
5  *
6  * \author Richard Heck
7  * 
8  * This code is based upon output_docbook.cpp
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "output_xhtml.h"
16
17 #include "Buffer.h"
18 #include "buffer_funcs.h"
19 #include "BufferParams.h"
20 #include "Counters.h"
21 #include "Layout.h"
22 #include "OutputParams.h"
23 #include "Paragraph.h"
24 #include "ParagraphList.h"
25 #include "ParagraphParameters.h"
26 #include "sgml.h"
27 #include "Text.h"
28 #include "TextClass.h"
29
30 #include "support/lassert.h"
31 #include "support/debug.h"
32 #include "support/lstrings.h"
33
34 #include <vector>
35
36 using namespace std;
37 using namespace lyx::support;
38
39 namespace lyx {
40
41 namespace html {
42
43 docstring escapeChar(char_type c)
44 {
45         docstring str;
46         switch (c) {
47         case ' ':
48                 str += " ";
49                 break;
50         case '&':
51                 str += "&amp;";
52                 break;
53         case '<':
54                 str += "&lt;";
55                 break;
56         case '>':
57                 str += "&gt;";
58                 break;
59         default:
60                 str += c;
61                 break;
62         }
63         return str;
64 }
65
66
67 // escape what needs escaping
68 docstring htmlize(docstring const & str) {
69         odocstringstream d;
70         docstring::const_iterator it = str.begin();
71         docstring::const_iterator en = str.end();
72         for (; it != en; ++it)
73                 d << escapeChar(*it);
74         return d.str();
75 }
76
77
78 bool isFontTag(string const & s)
79 {
80         return s == "em" || s == "strong"; // others?
81 }
82 } // namespace html
83
84
85 docstring StartTag::asTag() const
86 {
87         string output = "<" + tag_;
88         if (!attr_.empty())
89                 output += " " + attr_;
90         output += ">";
91         return from_utf8(output);
92 }
93
94
95 docstring StartTag::asEndTag() const
96 {
97         string output = "</" + tag_ + ">";
98         return from_utf8(output);
99 }
100
101
102 docstring EndTag::asEndTag() const
103 {
104         string output = "</" + tag_ + ">";
105         return from_utf8(output);
106 }
107
108
109 docstring CompTag::asTag() const
110 {
111         string output = "<" + tag_;
112         if (!attr_.empty())
113                 output += " " + attr_;
114         output += " />";
115         return from_utf8(output);
116 }
117
118
119 ////////////////////////////////////////////////////////////////
120 ///
121 /// XHTMLStream
122 ///
123 ////////////////////////////////////////////////////////////////
124
125 XHTMLStream::XHTMLStream(odocstream & os) 
126                 :os_(os)
127 {}
128
129
130 void XHTMLStream::cr() 
131 {
132         // tabs?
133         os_ << from_ascii("\n");
134 }
135
136
137 void XHTMLStream::writeError(std::string const & s)
138 {
139         LYXERR0(s);
140         os_ << from_utf8("<!-- Output Error: " + s + " -->");
141 }
142
143
144 bool XHTMLStream::closeFontTags()
145 {
146         if (tag_stack_.empty())
147                 return true;
148         // first, we close any open font tags we can close
149         StartTag curtag = tag_stack_.back();
150         while (html::isFontTag(curtag.tag_)) {
151                 os_ << curtag.asEndTag();
152                 tag_stack_.pop_back();
153                 if (tag_stack_.empty())
154                         // this probably shouldn't happen, since then the
155                         // font tags weren't in any other tag. but that
156                         // problem will likely be caught elsewhere.
157                         return true;
158                 curtag = tag_stack_.back();
159         }
160         // so we've hit a non-font tag. let's see if any of the
161         // remaining tags are font tags.
162         TagStack::const_iterator it = tag_stack_.begin();
163         TagStack::const_iterator en = tag_stack_.end();
164         bool noFontTags = true;
165         for (; it != en; ++it) {
166                 if (html::isFontTag(it->tag_)) {
167                         writeError("Font tag `" + it->tag_ + "' still open in closeFontTags().");
168                         noFontTags = false;
169                 }
170         }
171         return noFontTags;
172 }
173
174
175 void XHTMLStream::clearTagDeque()
176 {
177         while (!pending_tags_.empty()) {
178                 StartTag const & tag = pending_tags_.front();
179                 // tabs?
180                 os_ << tag.asTag();
181                 tag_stack_.push_back(tag);
182                 pending_tags_.pop_front();
183         }
184 }
185
186
187 XHTMLStream & XHTMLStream::operator<<(docstring const & d)
188 {
189         clearTagDeque();
190         if (nextraw_) {
191                 os_ << d;
192                 nextraw_ = false;
193         } else
194                 os_ << html::htmlize(d);
195         return *this;
196 }
197
198
199 XHTMLStream & XHTMLStream::operator<<(const char * s)
200 {
201         clearTagDeque();
202         docstring const d = from_ascii(s);
203         if (nextraw_) {
204                 os_ << d;
205                 nextraw_ = false;
206         } else
207                 os_ << html::htmlize(d);
208         return *this;
209 }
210
211
212 XHTMLStream & XHTMLStream::operator<<(char_type c)
213 {
214         clearTagDeque();
215         if (nextraw_) {
216                 os_ << c;
217                 nextraw_ = false;
218         } else
219                 os_ << html::escapeChar(c);
220         return *this;
221 }
222
223
224 XHTMLStream & XHTMLStream::operator<<(NextRaw const &) 
225
226         nextraw_ = true; 
227         return *this;
228 }
229
230
231 XHTMLStream & XHTMLStream::operator<<(StartTag const & tag) 
232 {
233         if (tag.tag_.empty())
234                 return *this;
235         pending_tags_.push_back(tag);
236         if (tag.keepempty_)
237                 clearTagDeque();
238         return *this;
239 }
240
241
242 XHTMLStream & XHTMLStream::operator<<(CompTag const & tag) 
243 {
244         if (tag.tag_.empty())
245                 return *this;
246         clearTagDeque();
247         // tabs?
248         os_ << tag.asTag();
249         return *this;
250 }
251
252
253 bool    XHTMLStream::isTagOpen(string const & stag)
254 {
255         TagStack::const_iterator sit = tag_stack_.begin();
256         TagStack::const_iterator const sen = tag_stack_.end();
257         for (; sit != sen; ++sit)
258                 // we could check for the
259                 if (sit->tag_ == stag) 
260                         return true;
261         return false;
262 }
263
264
265 // this is complicated, because we want to make sure that
266 // everything is properly nested. the code ought to make 
267 // sure of that, but we won't assert (yet) if we run into
268 // a problem. we'll just output error messages and try our
269 // best to make things work.
270 XHTMLStream & XHTMLStream::operator<<(EndTag const & etag)
271 {
272         if (etag.tag_.empty())
273                 return *this;
274         // first make sure we're not closing an empty tag
275         if (!pending_tags_.empty()) {
276                 StartTag const & stag = pending_tags_.back();
277                 if (etag.tag_ == stag.tag_)  {
278                         // we have <tag></tag>, so we discard it and remove it 
279                         // from the pending_tags_.
280                         pending_tags_.pop_back();
281                         return *this;
282                 }
283                 // there is a pending tag that isn't the one we are trying
284                 // to close. 
285                 // is this tag itself pending?
286                 // non-const iterators because we may call erase().
287                 TagDeque::iterator dit = pending_tags_.begin();
288                 TagDeque::iterator const den = pending_tags_.end();
289                 for (; dit != den; ++dit) {
290                         if (dit->tag_ == etag.tag_) {
291                                 // it was pending, so we just erase it
292                                 writeError("Tried to close pending tag `" + etag.tag_ 
293                                         + "' when other tags were pending. Tag discarded.");
294                                 pending_tags_.erase(dit);
295                                 return *this;
296                         }
297                 }
298                 // so etag isn't itself pending. is it even open?
299                 if (!isTagOpen(etag.tag_)) {
300                         writeError("Tried to close `" + etag.tag_ 
301                                  + "' when tag was not open. Tag discarded.");
302                         return *this;
303                 }
304                 // ok, so etag is open.
305                 // our strategy will be as below: we will do what we need to 
306                 // do to close this tag.
307                 string estr = "Closing tag `" + etag.tag_ 
308                         + "' when other tags are pending. Discarded pending tags:\n";
309                 for (dit = pending_tags_.begin(); dit != den; ++dit)
310                         estr += dit->tag_ + "\n";
311                 writeError(estr);
312                 // clear the pending tags...
313                 pending_tags_.clear();
314                 // ...and then just fall through.
315         }
316
317         // is the tag we are closing the last one we opened?
318         if (etag.tag_ == tag_stack_.back().tag_) {
319                 // output it...
320                 os_ << etag.asEndTag();
321                 // ...and forget about it
322                 tag_stack_.pop_back();
323                 return *this;
324         } 
325         
326         // we are trying to close a tag other than the one last opened. 
327         // let's first see if this particular tag is still open somehow.
328         if (!isTagOpen(etag.tag_)) {
329                 writeError("Tried to close `" + etag.tag_ 
330                         + "' when tag was not open. Tag discarded.");
331                 return *this;
332         }
333         
334         // so the tag was opened, but other tags have been opened since
335         // and not yet closed.
336         // if it's a font tag, though...
337         if (html::isFontTag(etag.tag_)) {
338                 // it won't be a problem if the other tags open since this one
339                 // are also font tags.
340                 TagStack::const_reverse_iterator rit = tag_stack_.rbegin();
341                 TagStack::const_reverse_iterator ren = tag_stack_.rend();
342                 for (; rit != ren; ++rit) {
343                         if (rit->tag_ == etag.tag_)
344                                 break;
345                         if (!html::isFontTag(rit->tag_)) {
346                                 // we'll just leave it and, presumably, have to close it later.
347                                 writeError("Unable to close font tag `" + etag.tag_ 
348                                         + "' due to open non-font tag `" + rit->tag_ + "'.");
349                                 return *this;
350                         }
351                 }
352                 
353                 // so we have e.g.:
354                 //    <em>this is <strong>bold
355                 // and are being asked to closed em. we want:
356                 //    <em>this is <strong>bold</strong></em><strong>
357                 // first, we close the intervening tags...
358                 StartTag curtag = tag_stack_.back();
359                 // ...remembering them in a stack.
360                 TagStack fontstack;
361                 while (curtag.tag_ != etag.tag_) {
362                         os_ << curtag.asEndTag();
363                         fontstack.push_back(curtag);
364                         tag_stack_.pop_back();
365                         curtag = tag_stack_.back();
366                 }
367                 // now close our tag...
368                 os_ << etag.asEndTag();
369                 tag_stack_.pop_back();
370
371                 // ...and restore the other tags.
372                 rit = fontstack.rbegin();
373                 ren = fontstack.rend();
374                 for (; rit != ren; ++rit)
375                         pending_tags_.push_back(*rit);
376                 return *this;
377         }
378         
379         // it wasn't a font tag.
380         // so other tags were opened before this one and not properly closed. 
381         // so we'll close them, too. that may cause other issues later, but it 
382         // at least guarantees proper nesting.
383         writeError("Closing tag `" + etag.tag_ 
384                 + "' when other tags are open, namely:");
385         StartTag curtag = tag_stack_.back();
386         while (curtag.tag_ != etag.tag_) {
387                 writeError(curtag.tag_);
388                 os_ << curtag.asEndTag();
389                 tag_stack_.pop_back();
390                 curtag = tag_stack_.back();
391         }
392         // curtag is now the one we actually want.
393         os_ << curtag.asEndTag();
394         tag_stack_.pop_back();
395         
396         return *this;
397 }
398
399 // End code for XHTMLStream
400
401 namespace {
402         
403 // convenience functions
404
405 inline void openTag(XHTMLStream & xs, Layout const & lay)
406 {
407         xs << StartTag(lay.htmltag(), lay.htmlattr());
408 }
409
410
411 inline void closeTag(XHTMLStream & xs, Layout const & lay)
412 {
413         xs << EndTag(lay.htmltag());
414 }
415
416
417 inline void openLabelTag(XHTMLStream & xs, Layout const & lay)
418 {
419         xs << StartTag(lay.htmllabeltag(), lay.htmllabelattr());
420 }
421
422
423 inline void closeLabelTag(XHTMLStream & xs, Layout const & lay)
424 {
425         xs << EndTag(lay.htmllabeltag());
426 }
427
428
429 inline void openItemTag(XHTMLStream & xs, Layout const & lay)
430 {
431         xs << StartTag(lay.htmlitemtag(), lay.htmlitemattr(), true);
432 }
433
434
435 inline void closeItemTag(XHTMLStream & xs, Layout const & lay)
436 {
437         xs << EndTag(lay.htmlitemtag());
438 }
439
440 // end of convenience functions
441
442 ParagraphList::const_iterator searchParagraphHtml(
443         ParagraphList::const_iterator p,
444         ParagraphList::const_iterator const & pend)
445 {
446         for (++p; p != pend && p->layout().latextype == LATEX_PARAGRAPH; ++p)
447                 ;
448
449         return p;
450 }
451
452
453 ParagraphList::const_iterator searchEnvironmentHtml(
454                 ParagraphList::const_iterator const pstart,
455                 ParagraphList::const_iterator const & pend)
456 {
457         ParagraphList::const_iterator p = pstart;
458         Layout const & bstyle = p->layout();
459         size_t const depth = p->params().depth();
460         for (++p; p != pend; ++p) {
461                 Layout const & style = p->layout();
462                 // It shouldn't happen that e.g. a section command occurs inside
463                 // a quotation environment, at a higher depth, but as of 6/2009,
464                 // it can happen. We pretend that it's just at lowest depth.
465                 if (style.latextype == LATEX_COMMAND)
466                         return p;
467                 // If depth is down, we're done
468                 if (p->params().depth() < depth)
469                         return p;
470                 // If depth is up, we're not done
471                 if (p->params().depth() > depth)
472                         continue;
473                 // Now we know we are at the same depth
474                 if (style.latextype == LATEX_PARAGRAPH
475                     || style.latexname() != bstyle.latexname())
476                         return p;
477         }
478         return pend;
479 }
480
481
482 ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
483                                             XHTMLStream & xs,
484                                             OutputParams const & runparams,
485                                             Text const & text,
486                                             ParagraphList::const_iterator const & pbegin,
487                                             ParagraphList::const_iterator const & pend)
488 {
489         ParagraphList::const_iterator const begin = text.paragraphs().begin();
490         ParagraphList::const_iterator par = pbegin;
491         for (; par != pend; ++par) {
492                 Layout const & lay = par->layout();
493                 if (!lay.counter.empty())
494                         buf.params().documentClass().counters().step(lay.counter);
495                 // FIXME We should see if there's a label to be output and
496                 // do something with it.
497                 if (par != pbegin)
498                         xs.cr();
499
500                 // If we are already in a paragraph, and this is the first one, then we
501                 // do not want to open the paragraph tag.
502                 // we also do not want to open it if the current layout does not permit
503                 // multiple paragraphs.
504                 bool const opened = runparams.html_make_pars &&
505                         (par != pbegin || !runparams.html_in_par);
506                 if (opened)
507                         openTag(xs, lay);
508                 docstring const deferred = 
509                         par->simpleLyXHTMLOnePar(buf, xs, runparams, text.outerFont(distance(begin, par)));
510
511                 // We want to issue the closing tag if either:
512                 //   (i)  We opened it, and either html_in_par is false,
513                 //        or we're not in the last paragraph, anyway.
514                 //   (ii) We didn't open it and html_in_par is true, 
515                 //        but we are in the first par, and there is a next par.
516                 ParagraphList::const_iterator nextpar = par;
517                 nextpar++;
518                 bool const needclose = 
519                         (opened && (!runparams.html_in_par || nextpar != pend))
520                         || (!opened && runparams.html_in_par && par == pbegin && nextpar != pend);
521                 if (needclose) {
522                         closeTag(xs, lay);
523                         xs.cr();
524                 }
525                 if (!deferred.empty()) {
526                         xs << XHTMLStream::NextRaw() << deferred;
527                         xs.cr();
528                 }
529         }
530         return pend;
531 }
532
533
534 ParagraphList::const_iterator makeBibliography(Buffer const & buf,
535                                 XHTMLStream & xs,
536                                 OutputParams const & runparams,
537                                 Text const & text,
538                                 ParagraphList::const_iterator const & pbegin,
539                                 ParagraphList::const_iterator const & pend) 
540 {
541         xs << StartTag("h2", "class='bibliography'");
542         xs << pbegin->layout().labelstring(false);
543         xs << EndTag("h2");
544         xs.cr();
545         xs << StartTag("div", "class='bibliography'");
546         xs.cr();
547         makeParagraphs(buf, xs, runparams, text, pbegin, pend);
548         xs << EndTag("div");
549         return pend;
550 }
551
552
553 bool isNormalEnv(Layout const & lay)
554 {
555         return lay.latextype == LATEX_ENVIRONMENT;
556 }
557
558         
559 ParagraphList::const_iterator makeEnvironmentHtml(Buffer const & buf,
560                                               XHTMLStream & xs,
561                                               OutputParams const & runparams,
562                                               Text const & text,
563                                               ParagraphList::const_iterator const & pbegin,
564                                               ParagraphList::const_iterator const & pend) 
565 {
566         ParagraphList::const_iterator const begin = text.paragraphs().begin();
567         ParagraphList::const_iterator par = pbegin;
568         Layout const & bstyle = par->layout();
569         depth_type const origdepth = pbegin->params().depth();
570
571         // open tag for this environment
572         openTag(xs, bstyle);
573         xs.cr();
574
575         // we will on occasion need to remember a layout from before.
576         Layout const * lastlay = 0;
577
578         while (par != pend) {
579                 Layout const & style = par->layout();
580                 // the counter only gets stepped if we're in some kind of list,
581                 // or if it's the first time through.
582                 // note that enum, etc, are handled automatically.
583                 // FIXME There may be a bug here about user defined enumeration
584                 // types. If so, then we'll need to take the counter and add "i",
585                 // "ii", etc, as with enum.
586                 if (!style.counter.empty() && 
587                     (par == pbegin || !isNormalEnv(style))
588                     && style.latextype == LATEX_LIST_ENVIRONMENT)
589                         buf.params().documentClass().counters().step(style.counter);
590                 ParagraphList::const_iterator send;
591                 // this will be positive, if we want to skip the initial word
592                 // (if it's been taken for the label).
593                 pos_type sep = 0;
594
595                 switch (style.latextype) {
596                 case LATEX_ENVIRONMENT:
597                 case LATEX_LIST_ENVIRONMENT:
598                 case LATEX_ITEM_ENVIRONMENT: {
599                         // There are two possiblities in this case. 
600                         // One is that we are still in the environment in which we 
601                         // started---which we will be if the depth is the same.
602                         if (par->params().depth() == origdepth) {
603                                 LASSERT(bstyle == style, /* */);
604                                 if (lastlay != 0) {
605                                         closeItemTag(xs, *lastlay);
606                                         lastlay = 0;
607                                 }
608                                 bool const labelfirst = style.htmllabelfirst();
609                                 if (isNormalEnv(style)) {
610                                         // in this case, we print the label only for the first 
611                                         // paragraph (as in a theorem).
612                                         openItemTag(xs, style);
613                                         if (par == pbegin && style.htmllabeltag() != "NONE") {
614                                                 docstring const lbl = 
615                                                                 pbegin->expandLabel(style, buf.params(), false);
616                                                 if (!lbl.empty()) {
617                                                         openLabelTag(xs, style);
618                                                         xs << lbl;
619                                                         closeLabelTag(xs, style);
620                                                 }
621                                                 xs.cr();
622                                         }
623                                 }       else { // some kind of list
624                                         if (!labelfirst)
625                                                 openItemTag(xs, style);
626                                         if (style.labeltype == LABEL_MANUAL
627                                             && style.htmllabeltag() != "NONE") {
628                                                 openLabelTag(xs, style);
629 //                                              sep = par->firstWordLyXHTML(xs, runparams);
630                                                 closeLabelTag(xs, style);
631                                                 xs.cr();
632                                         }
633                                         else if (style.labeltype != LABEL_NO_LABEL
634                                                  && style.htmllabeltag() != "NONE") {
635                                                 openLabelTag(xs, style);
636                                                 xs << par->expandLabel(style, buf.params(), false);
637                                                 closeLabelTag(xs, style);
638                                                 xs.cr();
639                                         }
640                                         if (labelfirst)
641                                                 openItemTag(xs, style);
642                                 }
643                                 par->simpleLyXHTMLOnePar(buf, xs, runparams, 
644                                         text.outerFont(distance(begin, par)), sep);
645                                 ++par;
646                                 // We may not want to close the tag yet, in particular,
647                                 // if we're not at the end...
648                                 if (par != pend 
649                                         //  and are doing items...
650                                          && !isNormalEnv(style)
651                                          // and if the depth has changed...
652                                          && par->params().depth() != origdepth) {
653                                          // then we'll save this layout for later, and close it when
654                                          // we get another item.
655                                         lastlay = &style;
656                                 } else
657                                         closeItemTag(xs, style);
658                                 xs.cr();
659                         }
660                         // The other possibility is that the depth has increased, in which
661                         // case we need to recurse.
662                         else {
663                                 send = searchEnvironmentHtml(par, pend);
664                                 par = makeEnvironmentHtml(buf, xs, runparams, text, par, send);
665                         }
666                         break;
667                 }
668                 case LATEX_PARAGRAPH:
669                         send = searchParagraphHtml(par, pend);
670                         par = makeParagraphs(buf, xs, runparams, text, par, send);
671                         break;
672                 // Shouldn't happen
673                 case LATEX_BIB_ENVIRONMENT:
674                         send = par;
675                         ++send;
676                         par = makeParagraphs(buf, xs, runparams, text, par, send);
677                         break;
678                 // Shouldn't happen
679                 case LATEX_COMMAND:
680                         ++par;
681                         break;
682                 }
683         }
684
685         if (lastlay != 0)
686                 closeItemTag(xs, *lastlay);
687         closeTag(xs, bstyle);
688         xs.cr();
689         return pend;
690 }
691
692
693 void makeCommand(Buffer const & buf,
694                                           XHTMLStream & xs,
695                                           OutputParams const & runparams,
696                                           Text const & text,
697                                           ParagraphList::const_iterator const & pbegin)
698 {
699         Layout const & style = pbegin->layout();
700         if (!style.counter.empty())
701                 buf.params().documentClass().counters().step(style.counter);
702
703         openTag(xs, style);
704
705         // Label around sectioning number:
706         // FIXME Probably need to account for LABEL_MANUAL
707         if (style.labeltype != LABEL_NO_LABEL) {
708                 openLabelTag(xs, style);
709                 xs << pbegin->expandLabel(style, buf.params(), false);
710                 closeLabelTag(xs, style);
711                 // Otherwise the label might run together with the text
712                 xs << from_ascii(" ");
713         }
714
715         ParagraphList::const_iterator const begin = text.paragraphs().begin();
716         pbegin->simpleLyXHTMLOnePar(buf, xs, runparams,
717                         text.outerFont(distance(begin, pbegin)));
718         closeTag(xs, style);
719         xs.cr();
720 }
721
722 } // end anonymous namespace
723
724
725 void xhtmlParagraphs(Text const & text,
726                        Buffer const & buf,
727                        XHTMLStream & xs,
728                        OutputParams const & runparams)
729 {
730         ParagraphList const & paragraphs = text.paragraphs();
731         ParagraphList::const_iterator par = paragraphs.begin();
732         ParagraphList::const_iterator pend = paragraphs.end();
733
734         OutputParams ourparams = runparams;
735         while (par != pend) {
736                 Layout const & style = par->layout();
737                 ParagraphList::const_iterator lastpar = par;
738                 ParagraphList::const_iterator send;
739
740                 switch (style.latextype) {
741                 case LATEX_COMMAND: {
742                         // The files with which we are working never have more than
743                         // one paragraph in a command structure.
744                         // FIXME 
745                         // if (ourparams.html_in_par)
746                         //   fix it so we don't get sections inside standard, e.g.
747                         // note that we may then need to make runparams not const, so we
748                         // can communicate that back.
749                         // FIXME Maybe this fix should be in the routines themselves, in case
750                         // they are called from elsewhere.
751                         makeCommand(buf, xs, ourparams, text, par);
752                         ++par;
753                         break;
754                 }
755                 case LATEX_ENVIRONMENT:
756                 case LATEX_LIST_ENVIRONMENT:
757                 case LATEX_ITEM_ENVIRONMENT: {
758                         // FIXME Same fix here.
759                         send = searchEnvironmentHtml(par, pend);
760                         par = makeEnvironmentHtml(buf, xs, ourparams, text, par, send);
761                         break;
762                 }
763                 case LATEX_BIB_ENVIRONMENT: {
764                         // FIXME Same fix here.
765                         send = searchEnvironmentHtml(par, pend);
766                         par = makeBibliography(buf, xs, ourparams, text, par, send);
767                         break;
768                 }
769                 case LATEX_PARAGRAPH:
770                         send = searchParagraphHtml(par, pend);
771                         par = makeParagraphs(buf, xs, ourparams, text, par, send);
772                         break;
773                 }
774                 // FIXME??
775                 // makeEnvironment may process more than one paragraphs and bypass pend
776                 if (distance(lastpar, par) >= distance(lastpar, pend))
777                         break;
778         }
779 }
780
781
782 } // namespace lyx