#include <config.h>
+#include "InsetFloat.h"
+
#include "InsetBox.h"
#include "InsetCaption.h"
-#include "InsetFloat.h"
#include "InsetGraphics.h"
#include "InsetLabel.h"
}
+/// Takes an unstructured subfigure container (typically, an InsetBox) and find the elements within:
+/// actual content (image or table), maybe a caption, maybe a label.
+std::tuple<InsetCode, const Inset *, const InsetCaption *, const InsetLabel *> docbookParseHopelessSubfigure(const InsetText * subfigure)
+{
+ InsetCode type = NO_CODE;
+ const Inset * content = nullptr;
+ const InsetCaption * caption = nullptr;
+ const InsetLabel * label = nullptr;
+
+ for (const auto & it : subfigure->paragraphs()) {
+ for (pos_type posIn = 0; posIn < it.size(); ++posIn) {
+ const Inset * inset = it.getInset(posIn);
+ if (inset) {
+ switch (inset->lyxCode()) {
+ case GRAPHICS_CODE:
+ case TABULAR_CODE:
+ if (!content) {
+ content = inset;
+ type = inset->lyxCode();
+ }
+ break;
+ case CAPTION_CODE:
+ if (!caption) {
+ caption = dynamic_cast<const InsetCaption *>(inset);
+
+ // A label often hides in a caption. Make a simplified version of the main loop.
+ if (!label) {
+ for (const auto &cit : caption->paragraphs()) {
+ for (pos_type cposIn = 0; cposIn < cit.size(); ++cposIn) {
+ const Inset *cinset = cit.getInset(posIn);
+ if (cinset && cinset->lyxCode() == LABEL_CODE) {
+ label = dynamic_cast<const InsetLabel *>(cinset);
+ break;
+ }
+ }
+
+ if (label)
+ break;
+ }
+ }
+ }
+ break;
+ case LABEL_CODE:
+ if (!label)
+ label = dynamic_cast<const InsetLabel *>(inset);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (content && caption && label)
+ break;
+ }
+
+ return std::make_tuple(type, content, caption, label);
+}
+
+
void docbookSubfigures(XMLStream & xs, OutputParams const & runparams, const InsetCaption * caption,
- const InsetLabel * label, std::vector<const InsetCollapsible *> & subfigures)
+ const InsetLabel * label, std::vector<const InsetCollapsible *> const & subfigures)
{
// Ensure there is no label output, it is supposed to be handled as xml:id.
OutputParams rpNoLabel = runparams;
xs << xml::StartTag("formalgroup", attr);
xs << xml::CR();
- xs << xml::StartTag("title", attr);
+ xs << xml::StartTag("title"); // Don't take attr here, the ID should only go in one place, not two.
if (caption) {
caption->getCaptionAsDocBook(xs, rpNoLabel);
} else {
// Deal with each subfigure individually. This should also deal with their caption and their label.
// This should be a recursive call to InsetFloat.
- // An item in subfigure should either be an InsetBox containing an InsetFloat or directly an InsetFloat.
- for (const InsetCollapsible *subfigure: subfigures) {
- // If there is no InsetFloat in the paragraphs, output a warning.
- bool foundInsetFloat = false;
+ // An item in subfigure should either be an InsetBox containing an InsetFloat, or an InsetBox directly containing
+ // an image or a table, or directly an InsetFloat.
+ for (const InsetCollapsible * subfigure: subfigures) {
+ if (subfigure == nullptr)
+ continue;
// The collapsible may already be a float (InsetFloat).
- if (subfigure && dynamic_cast<const InsetFloat *>(subfigure))
- foundInsetFloat = true;
+ if (dynamic_cast<const InsetFloat *>(subfigure)) {
+ subfigure->docbook(xs, runparams);
+ continue;
+ }
- // Subfigures are in boxes.
- if (!foundInsetFloat) {
+ // Subfigures are in boxes, then in InsetFloat.
+ {
+ bool foundInsetFloat = false;
for (const auto &it : subfigure->paragraphs()) {
for (pos_type posIn = 0; posIn < it.size(); ++posIn) {
const Inset *inset = it.getInset(posIn);
- if (inset && dynamic_cast<const InsetFloat *>(inset)) {
+ if (inset && inset->lyxCode() == FLOAT_CODE) {
foundInsetFloat = true;
+ inset->docbook(xs, runparams);
break;
}
}
if (foundInsetFloat)
break;
}
+ if (foundInsetFloat)
+ continue;
+ }
+
+ // Subfigures are in boxes, then directly an image or a table. In that case, generate the whole content of the
+ // InsetBox, but not the box container.
+ // Impose some model on the subfigure: at most a caption, at most a label, exactly one figure or one table.
+ {
+ InsetCode stype = NO_CODE;
+ const Inset * scontent = nullptr;
+ const InsetCaption * scaption = nullptr;
+ const InsetLabel * slabel = nullptr;
+
+ std::tie(stype, scontent, scaption, slabel) = docbookParseHopelessSubfigure(subfigure);
+
+ // If there is something, generate it. This is very much like docbookNoSubfigures, but many things
+ // must be coded differently because there is no float.
+ // TODO: some code is identical to Floating, like Floating::docbookTag or Floating::docbookCaption. How to reuse that code?
+ if (scontent) {
+ // Floating::docbookCaption()
+ string docbook_caption = "caption"; // This is already correct for tables.
+ if (stype == GRAPHICS_CODE)
+ docbook_caption = "title";
+
+ // Floating::docbookTag() with hasTitle = true, as we are in formalgroup.
+ string stag = "float";
+ if (stype == GRAPHICS_CODE)
+ stag = "figure";
+ else if (stype == TABULAR_CODE)
+ stag = "table";
+
+ // Ensure there is no label output, it is supposed to be handled as xml:id.
+ if (slabel)
+ rpNoLabel.docbook_anchors_to_ignore.emplace(slabel->screenLabel());
+
+ // Ensure the float does not output its caption, as it is handled here (DocBook mandates a specific place for
+ // captions, they cannot appear at the end of the float, albeit LyX is happy with that).
+ OutputParams rpNoTitle = runparams;
+ rpNoTitle.docbook_in_float = true;
+ if (stype == TABULAR_CODE)
+ rpNoTitle.docbook_in_table = true;
+
+ // Organisation: <float> <title if any/> <contents without title/> </float>.
+ docstring sattr = docstring();
+ if (slabel)
+ sattr += "xml:id=\"" + xml::cleanID(slabel->screenLabel()) + "\"";
+ // No layout way of adding attributes, unlike the normal code path.
+
+ xs << xml::StartTag(stag, sattr);
+ xs << xml::CR();
+ xs << xml::StartTag(docbook_caption);
+ if (scaption)
+ scaption->getCaptionAsDocBook(xs, rpNoLabel);
+ else // Mandatory in formalgroup.
+ xs << "No caption detected";
+ xs << xml::EndTag(docbook_caption);
+ xs << xml::CR();
+ scontent->docbook(xs, rpNoTitle);
+ xs << xml::EndTag(stag);
+ xs << xml::CR();
+
+ // This subfigure could be generated.
+ continue;
+ }
}
- if (!foundInsetFloat)
- xs << XMLStream::ESCAPE_NONE << "Error: no float found in the box. "
- "To use subfigures in DocBook, elements must be wrapped in a float "
- "inset and have a title/caption.";
+ // If there is no InsetFloat in the inset, output a warning.
+ xs << XMLStream::ESCAPE_NONE << "Error: no float found in the box. "
+ "To use subfigures in DocBook, elements must be wrapped in a float "
+ "inset and have a title/caption.";
// TODO: could also output a table, that would ensure that the document is correct and *displays* correctly (but without the right semantics), instead of just an error.
- // Finally, recurse.
+ // Recurse to generate as much content as possible (avoid any loss).
subfigure->docbook(xs, runparams);
}