1 /**
2  * \file output_docbook.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author José Matos
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11 
12 #include <config.h>
13 
14 #include "output_docbook.h"
15 
16 #include "Buffer.h"
17 #include "buffer_funcs.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 "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 #include "support/lyxalgo.h"
34 
35 #include <iostream>
36 
37 using namespace std;
38 using namespace lyx::support;
39 
40 namespace lyx {
41 
42 namespace {
43 
searchParagraph(ParagraphList::const_iterator p,ParagraphList::const_iterator const & pend)44 ParagraphList::const_iterator searchParagraph(
45 	ParagraphList::const_iterator p,
46 	ParagraphList::const_iterator const & pend)
47 {
48 	for (++p; p != pend && p->layout().latextype == LATEX_PARAGRAPH; ++p)
49 		;
50 
51 	return p;
52 }
53 
54 
searchCommand(ParagraphList::const_iterator p,ParagraphList::const_iterator const & pend)55 ParagraphList::const_iterator searchCommand(
56 	ParagraphList::const_iterator p,
57 	ParagraphList::const_iterator const & pend)
58 {
59 	Layout const & bstyle = p->layout();
60 
61 	for (++p; p != pend; ++p) {
62 		Layout const & style = p->layout();
63 		if (style.latextype == LATEX_COMMAND
64 				&& style.commanddepth <= bstyle.commanddepth)
65 			return p;
66 	}
67 	return pend;
68 }
69 
70 
searchEnvironment(ParagraphList::const_iterator p,ParagraphList::const_iterator const & pend)71 ParagraphList::const_iterator searchEnvironment(
72 	ParagraphList::const_iterator p,
73 	ParagraphList::const_iterator const & pend)
74 {
75 	Layout const & bstyle = p->layout();
76 	size_t const depth = p->params().depth();
77 	for (++p; p != pend; ++p) {
78 		Layout const & style = p->layout();
79 		if (style.latextype == LATEX_COMMAND)
80 			return p;
81 
82 		if (style.latextype == LATEX_PARAGRAPH) {
83 			if (p->params().depth() > depth)
84 				continue;
85 			return p;
86 		}
87 
88 		if (p->params().depth() < depth)
89 			return p;
90 
91 		if (style.latexname() != bstyle.latexname()
92 				&& p->params().depth() == depth)
93 			return p;
94 	}
95 	return pend;
96 }
97 
98 
makeParagraph(Buffer const & buf,odocstream & os,OutputParams const & runparams,Text const & text,ParagraphList::const_iterator const & pbegin,ParagraphList::const_iterator const & pend)99 ParagraphList::const_iterator makeParagraph(
100 	Buffer const & buf,
101 	odocstream & os,
102 	OutputParams const & runparams,
103 	Text const & text,
104 	ParagraphList::const_iterator const & pbegin,
105 	ParagraphList::const_iterator const & pend)
106 {
107 	ParagraphList const & paragraphs = text.paragraphs();
108 	for (ParagraphList::const_iterator par = pbegin; par != pend; ++par) {
109 		if (par != pbegin)
110 			os << '\n';
111 		bool const default_or_plain =
112 			(buf.params().documentClass().isDefaultLayout(par->layout())
113 				|| buf.params().documentClass().isPlainLayout(par->layout()));
114 		if (default_or_plain && par->emptyTag()) {
115 			par->simpleDocBookOnePar(buf, os, runparams,
116 					text.outerFont(distance(paragraphs.begin(), par)));
117 		} else {
118 			sgml::openTag(buf, os, runparams, *par);
119 			par->simpleDocBookOnePar(buf, os, runparams,
120 					text.outerFont(distance(paragraphs.begin(), par)));
121 			sgml::closeTag(os, *par);
122 		}
123 	}
124 	return pend;
125 }
126 
127 
makeEnvironment(Buffer const & buf,odocstream & os,OutputParams const & runparams,Text const & text,ParagraphList::const_iterator const & pbegin,ParagraphList::const_iterator const & pend)128 ParagraphList::const_iterator makeEnvironment(
129 	Buffer const & buf,
130 	odocstream & os,
131 	OutputParams const & runparams,
132 	Text const & text,
133 	ParagraphList::const_iterator const & pbegin,
134 	ParagraphList::const_iterator const & pend)
135 {
136 	ParagraphList const & paragraphs = text.paragraphs();
137 	ParagraphList::const_iterator par = pbegin;
138 
139 	Layout const & defaultstyle = buf.params().documentClass().defaultLayout();
140 	Layout const & bstyle = par->layout();
141 
142 	// Opening outter tag
143 	sgml::openTag(buf, os, runparams, *pbegin);
144 	os << '\n';
145 	if (bstyle.latextype == LATEX_ENVIRONMENT && bstyle.pass_thru)
146 		os << "<![CDATA[";
147 
148 	while (par != pend) {
149 		Layout const & style = par->layout();
150 		ParagraphList::const_iterator send;
151 		string id = par->getID(buf, runparams);
152 		string wrapper = "";
153 		pos_type sep = 0;
154 
155 		// Opening inner tag
156 		switch (bstyle.latextype) {
157 		case LATEX_ENVIRONMENT:
158 			if (!bstyle.innertag().empty()) {
159 				sgml::openTag(os, bstyle.innertag(), id);
160 			}
161 			break;
162 
163 		case LATEX_ITEM_ENVIRONMENT:
164 			if (!bstyle.labeltag().empty()) {
165 				sgml::openTag(os, bstyle.innertag(), id);
166 				sgml::openTag(os, bstyle.labeltag());
167 				sep = par->firstWordDocBook(os, runparams) + 1;
168 				sgml::closeTag(os, bstyle.labeltag());
169 			}
170 			wrapper = defaultstyle.latexname();
171 			// If a sub list (embedded list) appears next with a
172 			// different depth, then there is no need to open
173 			// another tag at the current depth.
174 			if(par->params().depth() == pbegin->params().depth()) {
175 				sgml::openTag(os, bstyle.itemtag());
176 			}
177 			break;
178 		default:
179 			break;
180 		}
181 
182 		switch (style.latextype) {
183 		case LATEX_ENVIRONMENT:
184 		case LATEX_ITEM_ENVIRONMENT: {
185 			if (par->params().depth() == pbegin->params().depth()) {
186 				sgml::openTag(os, wrapper);
187 				par->simpleDocBookOnePar(buf, os, runparams,
188 					text.outerFont(distance(paragraphs.begin(), par)), sep);
189 				sgml::closeTag(os, wrapper);
190 				++par;
191 			}
192 			else {
193 				send = searchEnvironment(par, pend);
194 				par = makeEnvironment(buf, os, runparams, text, par,send);
195 			}
196 			break;
197 		}
198 		case LATEX_PARAGRAPH:
199 			send = searchParagraph(par, pend);
200 			par = makeParagraph(buf, os, runparams, text, par,send);
201 			break;
202 		case LATEX_LIST_ENVIRONMENT:
203 		case LATEX_BIB_ENVIRONMENT:
204 		case LATEX_COMMAND:
205 			// FIXME This means that we are just skipping any paragraph that
206 			// isn't implemented above, and this includes lists.
207 			++par;
208 			break;
209 		}
210 
211 		// Closing inner tag
212 		switch (bstyle.latextype) {
213 		case LATEX_ENVIRONMENT:
214 			if (!bstyle.innertag().empty()) {
215 				sgml::closeTag(os, bstyle.innertag());
216 				os << '\n';
217 			}
218 			break;
219 		case LATEX_ITEM_ENVIRONMENT:
220 			// If a sub list (embedded list) appears next, then
221 			// there is no need to close the current tag.
222 			// par should have already been incremented to the next
223 			// element. So we can compare the depth of the next
224 			// element with pbegin.
225 			// We need to be careful, that we don't dereference par
226 			// when par == pend but at the same time that the
227 			// current tag is closed.
228 			if((par != pend && par->params().depth() == pbegin->params().depth()) || par == pend) {
229 				sgml::closeTag(os, bstyle.itemtag());
230 			}
231 			if (!bstyle.labeltag().empty())
232 				sgml::closeTag(os, bstyle.innertag());
233 			break;
234 		default:
235 			break;
236 		}
237 	}
238 
239 	if (bstyle.latextype == LATEX_ENVIRONMENT && bstyle.pass_thru)
240 		os << "]]>";
241 
242 	// Closing outer tag
243 	sgml::closeTag(os, *pbegin);
244 
245 	return pend;
246 }
247 
248 
makeCommand(Buffer const & buf,odocstream & os,OutputParams const & runparams,Text const & text,ParagraphList::const_iterator const & pbegin,ParagraphList::const_iterator const & pend)249 ParagraphList::const_iterator makeCommand(
250 	Buffer const & buf,
251 	odocstream & os,
252 	OutputParams const & runparams,
253 	Text const & text,
254 	ParagraphList::const_iterator const & pbegin,
255 	ParagraphList::const_iterator const & pend)
256 {
257 	ParagraphList const & paragraphs = text.paragraphs();
258 	ParagraphList::const_iterator par = pbegin;
259 	Layout const & bstyle = par->layout();
260 
261 	//Open outter tag
262 	sgml::openTag(buf, os, runparams, *pbegin);
263 	os << '\n';
264 
265 	// Label around sectioning number:
266 	if (!bstyle.labeltag().empty()) {
267 		sgml::openTag(os, bstyle.labeltag());
268 		// We don't care about appendix in DOCBOOK.
269 		os << par->expandDocBookLabel(bstyle, buf.params());
270 		sgml::closeTag(os, bstyle.labeltag());
271 	}
272 
273 	// Opend inner tag and	close inner tags
274 	sgml::openTag(os, bstyle.innertag());
275 	par->simpleDocBookOnePar(buf, os, runparams,
276 		text.outerFont(distance(paragraphs.begin(), par)));
277 	sgml::closeTag(os, bstyle.innertag());
278 	os << '\n';
279 
280 	++par;
281 	while (par != pend) {
282 		Layout const & style = par->layout();
283 		ParagraphList::const_iterator send;
284 
285 		switch (style.latextype) {
286 		case LATEX_COMMAND: {
287 			send = searchCommand(par, pend);
288 			par = makeCommand(buf, os, runparams, text, par,send);
289 			break;
290 		}
291 		case LATEX_ENVIRONMENT:
292 		case LATEX_ITEM_ENVIRONMENT: {
293 			send = searchEnvironment(par, pend);
294 			par = makeEnvironment(buf, os, runparams, text, par,send);
295 			break;
296 		}
297 		case LATEX_PARAGRAPH:
298 			send = searchParagraph(par, pend);
299 			par = makeParagraph(buf, os, runparams, text, par,send);
300 			break;
301 		case LATEX_BIB_ENVIRONMENT:
302 		case LATEX_LIST_ENVIRONMENT:
303 			// FIXME This means that we are just skipping any paragraph that
304 			// isn't implemented above.
305 			++par;
306 			break;
307 		}
308 	}
309 	// Close outter tag
310 	sgml::closeTag(os, *pbegin);
311 
312 	return pend;
313 }
314 
315 } // namespace
316 
317 
docbookParagraphs(Text const & text,Buffer const & buf,odocstream & os,OutputParams const & runparams)318 void docbookParagraphs(Text const & text,
319 		       Buffer const & buf,
320 		       odocstream & os,
321 		       OutputParams const & runparams)
322 {
323 	LASSERT(runparams.par_begin <= runparams.par_end,
324 		{ os << "<!-- Docbook Output Error -->\n"; return; });
325 
326 	ParagraphList const & paragraphs = text.paragraphs();
327 	ParagraphList::const_iterator par = paragraphs.begin();
328 	ParagraphList::const_iterator pend = paragraphs.end();
329 
330 	// if only part of the paragraphs will be outputed
331 	if (runparams.par_begin !=  runparams.par_end) {
332 		par = lyx::next(paragraphs.begin(), runparams.par_begin);
333 		pend = lyx::next(paragraphs.begin(), runparams.par_end);
334 		// runparams will be passed to nested paragraphs, so
335 		// we have to reset the range parameters.
336 		const_cast<OutputParams&>(runparams).par_begin = 0;
337 		const_cast<OutputParams&>(runparams).par_end = 0;
338 	}
339 
340 	while (par != pend) {
341 		Layout const & style = par->layout();
342 		ParagraphList::const_iterator lastpar = par;
343 		ParagraphList::const_iterator send;
344 
345 		switch (style.latextype) {
346 		case LATEX_COMMAND: {
347 			send = searchCommand(par, pend);
348 			par = makeCommand(buf, os, runparams, text, par, send);
349 			break;
350 		}
351 		case LATEX_ENVIRONMENT:
352 		case LATEX_ITEM_ENVIRONMENT: {
353 			send = searchEnvironment(par, pend);
354 			par = makeEnvironment(buf, os, runparams, text, par, send);
355 			break;
356 		}
357 		case LATEX_PARAGRAPH:
358 			send = searchParagraph(par, pend);
359 			par = makeParagraph(buf, os, runparams, text, par, send);
360 			break;
361 		case LATEX_BIB_ENVIRONMENT:
362 		case LATEX_LIST_ENVIRONMENT:
363 			// FIXME This means that we are just skipping any paragraph that
364 			// isn't implemented above.
365 			++par;
366 			break;
367 		}
368 		// makeEnvironment may process more than one paragraphs and bypass pend
369 		if (distance(lastpar, par) >= distance(lastpar, pend))
370 			break;
371 	}
372 }
373 
374 
375 } // namespace lyx
376