]> git.lyx.org Git - lyx.git/blob - src/output_xhtml.cpp
Remove unnecessary `c_str`
[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 Kimberly 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 "BufferParams.h"
19 #include "Counters.h"
20 #include "Font.h"
21 #include "Layout.h"
22 #include "Paragraph.h"
23 #include "ParagraphList.h"
24 #include "ParagraphParameters.h"
25 #include "xml.h"
26 #include "Text.h"
27 #include "TextClass.h"
28
29 #include "support/lassert.h"
30
31 #include <stack>
32 #include <iostream>
33
34 // Uncomment to activate debugging code.
35 // #define XHTML_DEBUG
36
37 using namespace std;
38 using namespace lyx::support;
39
40 namespace lyx {
41
42
43 docstring fontToHtmlTag(xml::FontTypes type)
44 {
45     switch(type) {
46         case xml::FontTypes::FT_EMPH:
47             return from_utf8("em");
48         case xml::FontTypes::FT_BOLD:
49             return from_utf8("b");
50         case xml::FontTypes::FT_NOUN:
51             return from_utf8("dfn");
52         case xml::FontTypes::FT_UBAR:
53         case xml::FontTypes::FT_WAVE:
54         case xml::FontTypes::FT_DBAR:
55             return from_utf8("u");
56         case xml::FontTypes::FT_SOUT:
57         case xml::FontTypes::FT_XOUT:
58             return from_utf8("del");
59         case xml::FontTypes::FT_ITALIC:
60             return from_utf8("i");
61         case xml::FontTypes::FT_UPRIGHT:
62         case xml::FontTypes::FT_SLANTED:
63         case xml::FontTypes::FT_SMALLCAPS:
64         case xml::FontTypes::FT_ROMAN:
65         case xml::FontTypes::FT_SANS:
66         case xml::FontTypes::FT_TYPE:
67         case xml::FontTypes::FT_SIZE_TINY:
68         case xml::FontTypes::FT_SIZE_SCRIPT:
69         case xml::FontTypes::FT_SIZE_FOOTNOTE:
70         case xml::FontTypes::FT_SIZE_SMALL:
71         case xml::FontTypes::FT_SIZE_NORMAL:
72         case xml::FontTypes::FT_SIZE_LARGE:
73         case xml::FontTypes::FT_SIZE_LARGER:
74         case xml::FontTypes::FT_SIZE_LARGEST:
75         case xml::FontTypes::FT_SIZE_HUGE:
76         case xml::FontTypes::FT_SIZE_HUGER:
77         case xml::FontTypes::FT_SIZE_INCREASE:
78         case xml::FontTypes::FT_SIZE_DECREASE:
79             return from_utf8("span");
80     }
81     // kill warning
82     return docstring();
83 }
84
85
86 docstring fontToHtmlAttribute(xml::FontTypes type)
87 {
88         switch(type) {
89         case xml::FontTypes::FT_EMPH:
90         case xml::FontTypes::FT_BOLD:
91                 return from_ascii("");
92         case xml::FontTypes::FT_NOUN:
93                 return from_ascii("class='lyxnoun'");
94         case xml::FontTypes::FT_UBAR:
95                 return from_ascii("");
96         case xml::FontTypes::FT_DBAR:
97                 return from_ascii("class='dline'");
98         case xml::FontTypes::FT_XOUT:
99         case xml::FontTypes::FT_SOUT:
100                 return from_ascii("class='strikeout'");
101         case xml::FontTypes::FT_WAVE:
102                 return from_ascii("class='wline'");
103         case xml::FontTypes::FT_ITALIC:
104                 return from_ascii("");
105         case xml::FontTypes::FT_UPRIGHT:
106                 return from_ascii("style='font-style:normal;'");
107         case xml::FontTypes::FT_SLANTED:
108                 return from_ascii("style='font-style:oblique;'");
109         case xml::FontTypes::FT_SMALLCAPS:
110                 return from_ascii("style='font-variant:small-caps;'");
111         case xml::FontTypes::FT_ROMAN:
112                 return from_ascii("style='font-family:serif;'");
113         case xml::FontTypes::FT_SANS:
114                 return from_ascii("style='font-family:sans-serif;'");
115         case xml::FontTypes::FT_TYPE:
116                 return from_ascii("style='font-family:monospace;'");
117         case xml::FontTypes::FT_SIZE_TINY:
118         case xml::FontTypes::FT_SIZE_SCRIPT:
119         case xml::FontTypes::FT_SIZE_FOOTNOTE:
120                 return from_ascii("style='font-size:x-small;'");
121         case xml::FontTypes::FT_SIZE_SMALL:
122                 return from_ascii("style='font-size:small;'");
123         case xml::FontTypes::FT_SIZE_NORMAL:
124                 return from_ascii("style='font-size:normal;'");
125         case xml::FontTypes::FT_SIZE_LARGE:
126                 return from_ascii("style='font-size:large;'");
127         case xml::FontTypes::FT_SIZE_LARGER:
128         case xml::FontTypes::FT_SIZE_LARGEST:
129                 return from_ascii("style='font-size:x-large;'");
130         case xml::FontTypes::FT_SIZE_HUGE:
131         case xml::FontTypes::FT_SIZE_HUGER:
132                 return from_ascii("style='font-size:xx-large;'");
133         case xml::FontTypes::FT_SIZE_INCREASE:
134                 return from_ascii("style='font-size:larger;'");
135         case xml::FontTypes::FT_SIZE_DECREASE:
136                 return from_ascii("style='font-size:smaller;'");
137         }
138         // kill warning
139         return from_ascii("");
140 }
141
142
143 xml::FontTag xhtmlStartFontTag(xml::FontTypes type)
144 {
145         return xml::FontTag(fontToHtmlTag(type), fontToHtmlAttribute(type), type);
146 }
147
148
149 xml::EndFontTag xhtmlEndFontTag(xml::FontTypes type)
150 {
151         return xml::EndFontTag(fontToHtmlTag(type), type);
152 }
153
154 namespace {
155
156 // convenience functions
157
158 inline void openParTag(XMLStream & xs, Layout const & lay,
159                        const std::string & parlabel)
160 {
161         string attrs = lay.htmlattr();
162         if (!parlabel.empty())
163                 attrs += " id='" + parlabel + "'";
164         xs << xml::ParTag(lay.htmltag(), attrs);
165 }
166
167
168 void openParTag(XMLStream & xs, Layout const & lay,
169                 ParagraphParameters const & params,
170                 const std::string & parlabel)
171 {
172         // FIXME Are there other things we should handle here?
173         string const align = alignmentToCSS(params.align());
174         if (align.empty()) {
175                 openParTag(xs, lay, parlabel);
176                 return;
177         }
178         string attrs = lay.htmlattr() + " style='text-align: " + align + ";'";
179         if (!parlabel.empty())
180                 attrs += " id='" + parlabel + "'";
181         xs << xml::ParTag(lay.htmltag(), attrs);
182 }
183
184
185 inline void closeTag(XMLStream & xs, Layout const & lay)
186 {
187         xs << xml::EndTag(lay.htmltag());
188 }
189
190
191 inline void openLabelTag(XMLStream & xs, Layout const & lay)
192 {
193         xs << xml::StartTag(lay.htmllabeltag(), lay.htmllabelattr());
194 }
195
196
197 inline void closeLabelTag(XMLStream & xs, Layout const & lay)
198 {
199         xs << xml::EndTag(lay.htmllabeltag());
200 }
201
202
203 inline void openItemTag(XMLStream & xs, Layout const & lay)
204 {
205         xs << xml::StartTag(lay.htmlitemtag(), lay.htmlitemattr(), true);
206 }
207
208
209 void openItemTag(XMLStream & xs, Layout const & lay,
210              ParagraphParameters const & params)
211 {
212         // FIXME Are there other things we should handle here?
213         string const align = alignmentToCSS(params.align());
214         if (align.empty()) {
215                 openItemTag(xs, lay);
216                 return;
217         }
218         string attrs = lay.htmlattr() + " style='text-align: " + align + ";'";
219         xs << xml::StartTag(lay.htmlitemtag(), attrs);
220 }
221
222
223 inline void closeItemTag(XMLStream & xs, Layout const & lay)
224 {
225         xs << xml::EndTag(lay.htmlitemtag());
226 }
227
228 // end of convenience functions
229
230 ParagraphList::const_iterator findLastParagraph(
231         ParagraphList::const_iterator p,
232         ParagraphList::const_iterator const & pend)
233 {
234         for (++p; p != pend && p->layout().latextype == LATEX_PARAGRAPH; ++p)
235                 ;
236
237         return p;
238 }
239
240
241 ParagraphList::const_iterator findEndOfEnvironment(
242                 ParagraphList::const_iterator const & pstart,
243                 ParagraphList::const_iterator const & pend)
244 {
245         ParagraphList::const_iterator p = pstart;
246         Layout const & bstyle = p->layout();
247         size_t const depth = p->params().depth();
248         for (++p; p != pend; ++p) {
249                 Layout const & style = p->layout();
250                 // It shouldn't happen that e.g. a section command occurs inside
251                 // a quotation environment, at a higher depth, but as of 6/2009,
252                 // it can happen. We pretend that it's just at lowest depth.
253                 if (style.latextype == LATEX_COMMAND)
254                         return p;
255
256                 // If depth is down, we're done
257                 if (p->params().depth() < depth)
258                         return p;
259
260                 // If depth is up, we're not done
261                 if (p->params().depth() > depth)
262                         continue;
263
264                 // FIXME I am not sure about the first check.
265                 // Surely we *could* have different layouts that count as
266                 // LATEX_PARAGRAPH, right?
267                 if (style.latextype == LATEX_PARAGRAPH || style != bstyle)
268                         return p;
269         }
270         return pend;
271 }
272
273
274 ParagraphList::const_iterator makeParagraphs(Buffer const & buf,
275                                             XMLStream & xs,
276                                             OutputParams const & runparams,
277                                             Text const & text,
278                                             ParagraphList::const_iterator const & pbegin,
279                                             ParagraphList::const_iterator const & pend)
280 {
281         ParagraphList::const_iterator const begin = text.paragraphs().begin();
282         ParagraphList::const_iterator par = pbegin;
283         for (; par != pend; ++par) {
284                 Layout const & lay = par->layout();
285                 if (!lay.counter.empty())
286                         buf.masterBuffer()->params().
287                             documentClass().counters().step(lay.counter, OutputUpdate);
288
289                 // FIXME We should see if there's a label to be output and
290                 // do something with it.
291                 if (par != pbegin)
292                         xs << xml::CR();
293
294                 // We want to open the paragraph tag if:
295                 //   (i) the current layout permits multiple paragraphs
296                 //  (ii) we are either not already inside a paragraph (HTMLIsBlock) OR
297                 //       we are, but this is not the first paragraph
298                 //
299                 // But there is also a special case, and we first see whether we are in it.
300                 // We do not want to open the paragraph tag if this paragraph contains
301                 // only one item, and that item is "inline", i.e., not HTMLIsBlock (such
302                 // as a branch). On the other hand, if that single item has a font change
303                 // applied to it, then we still do need to open the paragraph.
304                 //
305                 // Obviously, this is very fragile. The main reason we need to do this is
306                 // because of branches, e.g., a branch that contains an entire new section.
307                 // We do not really want to wrap that whole thing in a <div>...</div>.
308                 bool special_case = false;
309                 Inset const * specinset = par->size() == 1 ? par->getInset(0) : nullptr;
310                 if (specinset && !specinset->getLayout().htmlisblock()) {
311                         Layout const & style = par->layout();
312                         FontInfo const first_font = style.labeltype == LABEL_MANUAL ?
313                                                 style.labelfont : style.font;
314                         FontInfo const our_font =
315                                 par->getFont(buf.masterBuffer()->params(), 0,
316                                        text.outerFont(distance(begin, par))).fontInfo();
317                         if (first_font == our_font)
318                                 special_case = true;
319                 }
320
321                 bool const open_par = runparams.html_make_pars
322                         && (!runparams.html_in_par || par != pbegin)
323                         && !special_case;
324
325                 // We want to issue the closing tag if either:
326                 //   (i)  We opened it, and either html_in_par is false,
327                 //        or we're not in the last paragraph, anyway.
328                 //   (ii) We didn't open it and html_in_par is true,
329                 //        but we are in the first par, and there is a next par.
330                 ParagraphList::const_iterator nextpar = par;
331                 ++nextpar;
332                 bool const close_par =
333                         (open_par && (!runparams.html_in_par || nextpar != pend))
334                         || (!open_par && runparams.html_in_par && par == pbegin && nextpar != pend);
335
336                 if (open_par) {
337                         // We do not issue the paragraph id if we are doing
338                         // this for the TOC (or some similar purpose)
339                         openParTag(xs, lay, par->params(),
340                                    runparams.for_toc ? "" : par->magicLabel());
341                 }
342
343                 docstring const deferred = par->simpleLyXHTMLOnePar(buf, xs,
344                         runparams, text.outerFont(distance(begin, par)),
345                         open_par, close_par);
346
347                 if (close_par) {
348                         closeTag(xs, lay);
349                         xs << xml::CR();
350                 }
351
352                 if (!deferred.empty()) {
353                         xs << XMLStream::ESCAPE_NONE << deferred << xml::CR();
354                 }
355         }
356         return pend;
357 }
358
359
360 ParagraphList::const_iterator makeBibliography(Buffer const & buf,
361                                 XMLStream & xs,
362                                 OutputParams const & runparams,
363                                 Text const & text,
364                                 ParagraphList::const_iterator const & pbegin,
365                                 ParagraphList::const_iterator const & pend)
366 {
367         // FIXME XHTML
368         // Use TextClass::htmlTOCLayout() to figure out how we should look.
369         xs << xml::StartTag("h2", "class='bibliography'")
370            << pbegin->layout().labelstring(false)
371            << xml::EndTag("h2")
372            << xml::CR()
373            << xml::StartTag("div", "class='bibliography'")
374            << xml::CR();
375         makeParagraphs(buf, xs, runparams, text, pbegin, pend);
376         xs << xml::EndTag("div");
377         return pend;
378 }
379
380
381 bool isNormalEnv(Layout const & lay)
382 {
383         return lay.latextype == LATEX_ENVIRONMENT
384             || lay.latextype == LATEX_BIB_ENVIRONMENT;
385 }
386
387
388 ParagraphList::const_iterator makeEnvironment(Buffer const & buf,
389                                               XMLStream & xs,
390                                               OutputParams const & runparams,
391                                               Text const & text,
392                                               ParagraphList::const_iterator const & pbegin,
393                                               ParagraphList::const_iterator const & pend)
394 {
395         ParagraphList::const_iterator const begin = text.paragraphs().begin();
396         ParagraphList::const_iterator par = pbegin;
397         Layout const & bstyle = par->layout();
398         depth_type const origdepth = pbegin->params().depth();
399
400         // open tag for this environment
401         openParTag(xs, bstyle, pbegin->magicLabel());
402         xs << xml::CR();
403
404         // we will on occasion need to remember a layout from before.
405         Layout const * lastlay = nullptr;
406
407         while (par != pend) {
408                 Layout const & style = par->layout();
409                 // the counter only gets stepped if we're in some kind of list,
410                 // or if it's the first time through.
411                 // note that enum, etc, are handled automatically.
412                 // FIXME There may be a bug here about user defined enumeration
413                 // types. If so, then we'll need to take the counter and add "i",
414                 // "ii", etc, as with enum.
415                 Counters & cnts = buf.masterBuffer()->params().documentClass().counters();
416                 docstring const & cntr = style.counter;
417                 if (!style.counter.empty()
418                     && (par == pbegin || !isNormalEnv(style))
419                                 && cnts.hasCounter(cntr)
420                 )
421                         cnts.step(cntr, OutputUpdate);
422                 ParagraphList::const_iterator send;
423
424                 switch (style.latextype) {
425                 case LATEX_ENVIRONMENT:
426                 case LATEX_LIST_ENVIRONMENT:
427                 case LATEX_ITEM_ENVIRONMENT: {
428                         // There are two possibilities in this case.
429                         // One is that we are still in the environment in which we
430                         // started---which we will be if the depth is the same.
431                         if (par->params().depth() == origdepth) {
432                                 LATTEST(bstyle == style);
433                                 if (lastlay != nullptr) {
434                                         closeItemTag(xs, *lastlay);
435                                         lastlay = nullptr;
436                                 }
437
438                                 // this will be positive, if we want to skip the
439                                 // initial word (if it's been taken for the label).
440                                 pos_type sep = 0;
441                                 bool const labelfirst = style.htmllabelfirst();
442                                 if (!labelfirst)
443                                         openItemTag(xs, style, par->params());
444
445                                 // label output
446                                 if (style.labeltype != LABEL_NO_LABEL &&
447                                     style.htmllabeltag() != "NONE") {
448                                         if (isNormalEnv(style)) {
449                                                 // in this case, we print the label only for the first
450                                                 // paragraph (as in a theorem).
451                                                 if (par == pbegin) {
452                                                         docstring const lbl =
453                                                                         pbegin->params().labelString();
454                                                         if (!lbl.empty()) {
455                                                                 openLabelTag(xs, style);
456                                                                 xs << lbl;
457                                                                 closeLabelTag(xs, style);
458                                                         }
459                                                         xs << xml::CR();
460                                                 }
461                                         } else { // some kind of list
462                                                 if (style.labeltype == LABEL_MANUAL) {
463                                                         openLabelTag(xs, style);
464                                                         sep = par->firstWordLyXHTML(xs, runparams);
465                                                         closeLabelTag(xs, style);
466                                                         xs << xml::CR();
467                                                 }
468                                                 else {
469                                                         openLabelTag(xs, style);
470                                                         xs << par->params().labelString();
471                                                         closeLabelTag(xs, style);
472                                                         xs << xml::CR();
473                                                 }
474                                         }
475                                 } // end label output
476
477                                 if (labelfirst)
478                                         openItemTag(xs, style, par->params());
479
480                                 docstring deferred = par->simpleLyXHTMLOnePar(buf, xs, runparams,
481                                         text.outerFont(distance(begin, par)), true, true, sep);
482                                 xs << XMLStream::ESCAPE_NONE << deferred;
483                                 ++par;
484
485                                 // We may not want to close the tag yet, in particular:
486                                 // If we're not at the end...
487                                 if (par != pend
488                                         //  and are doing items...
489                                          && !isNormalEnv(style)
490                                          // and if the depth has changed...
491                                          && par->params().depth() != origdepth) {
492                                          // then we'll save this layout for later, and close it when
493                                          // we get another item.
494                                         lastlay = &style;
495                                 } else
496                                         closeItemTag(xs, style);
497                                 xs << xml::CR();
498                         }
499                         // The other possibility is that the depth has increased, in which
500                         // case we need to recurse.
501                         else {
502                                 send = findEndOfEnvironment(par, pend);
503                                 par = makeEnvironment(buf, xs, runparams, text, par, send);
504                         }
505                         break;
506                 }
507                 case LATEX_PARAGRAPH:
508                         send = findLastParagraph(par, pend);
509                         par = makeParagraphs(buf, xs, runparams, text, par, send);
510                         break;
511                 // Shouldn't happen
512                 case LATEX_BIB_ENVIRONMENT:
513                         send = par;
514                         ++send;
515                         par = makeParagraphs(buf, xs, runparams, text, par, send);
516                         break;
517                 // Shouldn't happen
518                 case LATEX_COMMAND:
519                         ++par;
520                         break;
521                 }
522         }
523
524         if (lastlay != nullptr)
525                 closeItemTag(xs, *lastlay);
526         closeTag(xs, bstyle);
527         xs << xml::CR();
528         return pend;
529 }
530
531
532 void makeCommand(Buffer const & buf,
533                  XMLStream & xs,
534                  OutputParams const & runparams,
535                  Text const & text,
536                  ParagraphList::const_iterator const & pbegin)
537 {
538         Layout const & style = pbegin->layout();
539         if (!style.counter.empty())
540                 buf.masterBuffer()->params().
541                     documentClass().counters().step(style.counter, OutputUpdate);
542
543         bool const make_parid = !runparams.for_toc && runparams.html_make_pars;
544
545         openParTag(xs, style, pbegin->params(),
546                    make_parid ? pbegin->magicLabel() : "");
547
548         // Label around sectioning number:
549         // FIXME Probably need to account for LABEL_MANUAL
550         // FIXME Probably also need now to account for labels ABOVE and CENTERED.
551         if (style.labeltype != LABEL_NO_LABEL) {
552                 openLabelTag(xs, style);
553                 xs << pbegin->params().labelString();
554                 closeLabelTag(xs, style);
555                 // Otherwise the label might run together with the text
556                 xs << from_ascii(" ");
557         }
558
559         ParagraphList::const_iterator const begin = text.paragraphs().begin();
560         pbegin->simpleLyXHTMLOnePar(buf, xs, runparams,
561                         text.outerFont(distance(begin, pbegin)));
562         closeTag(xs, style);
563         xs << xml::CR();
564 }
565
566 } // end anonymous namespace
567
568
569 void xhtmlParagraphs(Text const & text,
570                        Buffer const & buf,
571                        XMLStream & xs,
572                        OutputParams const & runparams)
573 {
574         ParagraphList const & paragraphs = text.paragraphs();
575         if (runparams.par_begin == runparams.par_end) {
576                 runparams.par_begin = 0;
577                 runparams.par_end = paragraphs.size();
578         }
579         pit_type bpit = runparams.par_begin;
580         pit_type const epit = runparams.par_end;
581         LASSERT(bpit < epit,
582                 { xs << XMLStream::ESCAPE_NONE << "<!-- XHTML output error! -->\n"; return; });
583
584         OutputParams ourparams = runparams;
585         ParagraphList::const_iterator const pend =
586                 (epit == (int) paragraphs.size()) ?
587                         paragraphs.end() : paragraphs.iterator_at(epit);
588         std::stack<int> headerLevels;
589
590         while (bpit < epit) {
591                 ParagraphList::const_iterator par = paragraphs.iterator_at(bpit);
592                 if (par->params().startOfAppendix()) {
593                         // We want to reset the counter corresponding to toplevel sectioning
594                         Layout const & lay =
595                                 buf.masterBuffer()->params().documentClass().getTOCLayout();
596                         docstring const cnt = lay.counter;
597                         if (!cnt.empty()) {
598                                 Counters & cnts =
599                                         buf.masterBuffer()->params().documentClass().counters();
600                                 cnts.reset(cnt);
601                         }
602                 }
603                 Layout const & style = par->layout();
604                 ParagraphList::const_iterator const lastpar = par;
605                 ParagraphList::const_iterator send;
606
607                 // Think about adding <section> and/or </section>s.
608                 // Document title is not in Sectioning, but rather in FrontMatter, so that it does not need to be taken
609                 // into account.
610                 if (style.category() == from_utf8("Sectioning")) {
611                         int level = style.toclevel;
612
613                         // Need to close a previous section if it has the same level or a higher one (close <section> if opening a
614                         // <h2> after a <h2>, <h3>, <h4>, <h5> or <h6>). More examples:
615                         //   - current: h2; back: h1; do not close any <section>
616                         //   - current: h1; back: h2; close two <section> (first the <h2>, then the <h1>, so a new <h1> can come)
617                         while (!headerLevels.empty() && level <= headerLevels.top()) {
618                                 // Output the tag only if it corresponds to a legit section.
619                                 int stackLevel = headerLevels.top();
620                                 if (stackLevel != Layout::NOT_IN_TOC) {
621                                         xs << xml::EndTag("section");
622                                         xs << xml::CR();
623                                 }
624                                 headerLevels.pop();
625                         }
626
627                         // Open the new section: first push it onto the stack, then output it in XHTML.
628                         headerLevels.push(level);
629                         // Some sectioning-like elements should not be output (such as FrontMatter).
630                         if (level != Layout::NOT_IN_TOC ) {
631                                 xs << xml::StartTag("section");
632                                 xs << xml::CR();
633                         }
634                 }
635
636                 switch (style.latextype) {
637                 case LATEX_COMMAND: {
638                         // The files with which we are working never have more than
639                         // one paragraph in a command structure.
640                         // FIXME
641                         // if (ourparams.html_in_par)
642                         //   fix it so we don't get sections inside standard, e.g.
643                         // note that we may then need to make runparams not const, so we
644                         // can communicate that back.
645                         // FIXME Maybe this fix should be in the routines themselves, in case
646                         // they are called from elsewhere.
647                         makeCommand(buf, xs, ourparams, text, par);
648                         ++par;
649                         break;
650                 }
651                 case LATEX_ENVIRONMENT:
652                 case LATEX_LIST_ENVIRONMENT:
653                 case LATEX_ITEM_ENVIRONMENT: {
654                         // FIXME Same fix here.
655                         send = findEndOfEnvironment(par, pend);
656                         par = makeEnvironment(buf, xs, ourparams, text, par, send);
657                         break;
658                 }
659                 case LATEX_BIB_ENVIRONMENT: {
660                         // FIXME Same fix here.
661                         send = findEndOfEnvironment(par, pend);
662                         par = makeBibliography(buf, xs, ourparams, text, par, send);
663                         break;
664                 }
665                 case LATEX_PARAGRAPH:
666                         send = findLastParagraph(par, pend);
667                         par = makeParagraphs(buf, xs, ourparams, text, par, send);
668                         break;
669                 }
670                 bpit += distance(lastpar, par);
671         }
672
673         // If need be, close <section>s, but only at the end of the document (otherwise, dealt with at the beginning
674         // of the loop).
675         while (!headerLevels.empty() && headerLevels.top() != Layout::NOT_IN_TOC) {
676                 headerLevels.pop();
677                 xs << xml::EndTag("section");
678                 xs << xml::CR();
679         }
680 }
681
682
683 string alignmentToCSS(LyXAlignment align)
684 {
685         switch (align) {
686         case LYX_ALIGN_BLOCK:
687                 // we are NOT going to use text-align: justify!!
688         case LYX_ALIGN_LEFT:
689                 return "left";
690         case LYX_ALIGN_RIGHT:
691                 return "right";
692         case LYX_ALIGN_CENTER:
693                 return "center";
694         default:
695                 break;
696         }
697         return "";
698 }
699
700 } // namespace lyx