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