]> git.lyx.org Git - lyx.git/commitdiff
Add support for CALS tables in DocBook.
authorThibaut Cuvelier <tcuvelier@lyx.org>
Mon, 13 Jul 2020 01:31:48 +0000 (03:31 +0200)
committerThibaut Cuvelier <tcuvelier@lyx.org>
Wed, 15 Jul 2020 22:40:16 +0000 (00:40 +0200)
development/FORMAT
lib/lyx2lyx/lyx_2_4.py
src/BufferParams.cpp
src/BufferParams.h
src/frontends/qt/GuiDocument.cpp
src/frontends/qt/ui/OutputUi.ui
src/insets/InsetTabular.cpp
src/insets/InsetTabular.h
src/tex2lyx/Preamble.cpp
src/tex2lyx/Preamble.h
src/version.h

index f903ed3ae969ccea03bc5a5b75f7970549d90fdc..b3968bf73ec86890fd2211d1fd44d26e6b741ce0 100644 (file)
@@ -7,6 +7,10 @@ changes happened in particular if possible. A good example would be
 
 -----------------------
 
+2020-07-14 Thibaut Cuvelier <tcuvelier@lyx.org>
+       * Format incremented to 598: DocBook can export to HTML and CALS tables, with the parameter
+          \docbook_table_output (0: HTML, only choice previously; CALS: 1).
+
 2020-03-13 Jürgen Spitzmüller <spitz@lyx.org>
        * Format incremented to 593: Buffer param \maintain_unincluded_children is not longer a bool
           (true|false) but has three states: "no" (formerly "false"), "strict" (formerly "true") and
index c4d63cab142034a4d8b64c61d3b1f46dcb418b8f..6283631d26effaf51ff2ee0b1c0132015f4329aa 100644 (file)
@@ -3922,6 +3922,20 @@ def revert_libertinus_sftt_fonts(document):
                     add_to_preamble(document, ["\\renewcommand*{\\LibertinusMono@scale}{" + str(tt_scale / 100.0) + "}"])
 
 
+def convert_docbook_table_output(document):
+    if find_token(document.header, '\\docbook_table_output') != -1:
+        document.warning("Malformed LyX file: \\docbook_table_output found before format 598!")
+    else:
+        document.header.append('\\docbook_table_output 0')
+
+
+def revert_docbook_table_output(document):
+    i = find_token(document.header, '\\docbook_table_output 0')
+    i = find_token(document.header, '\\docbook_table_output 1') if i == -1 else i
+    if i != -1:
+        del document.header[i]
+
+
 ##
 # Conversion hub
 #
@@ -3980,10 +3994,12 @@ convert = [
            [594, []],
            [595, []],
            [596, [convert_parskip]],
-           [597, [convert_libertinus_rm_fonts]]
+           [597, [convert_libertinus_rm_fonts]],
+           [598, [convert_docbook_table_output]]
           ]
 
-revert =  [[595, [revert_libertinus_rm_fonts,revert_libertinus_sftt_fonts]],
+revert =  [[597, [revert_docbook_table_output]],
+           [595, [revert_libertinus_rm_fonts,revert_libertinus_sftt_fonts]],
            [595, [revert_parskip,revert_line_vspaces]],
            [594, [revert_ams_spaces]],
            [593, [revert_counter_inset]],
index 4ed8b4ea9a68ef462bc59954b909a2af6a1592dd..722d7c1d8dd427d71af125f099a3e0e8d0da2f74 100644 (file)
@@ -471,6 +471,7 @@ BufferParams::BufferParams()
        html_math_output = MathML;
        html_math_img_scale = 1.0;
        html_css_as_file = false;
+       docbook_table_output = HTMLTable;
        display_pixel_ratio = 1.0;
 
        shell_escape = false;
@@ -1134,6 +1135,10 @@ string BufferParams::readToken(Lexer & lex, string const & token,
        } else if (token == "\\html_latex_end") {
                lex.eatLine();
                html_latex_end = lex.getString();
+       } else if (token == "\\docbook_table_output") {
+               int temp;
+               lex >> temp;
+               docbook_table_output = static_cast<TableOutput>(temp);
        } else if (token == "\\output_sync") {
                lex >> output_sync;
        } else if (token == "\\output_sync_macro") {
@@ -1491,6 +1496,8 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
           << "\\html_css_as_file " << html_css_as_file << '\n'
           << "\\html_be_strict " << convert<string>(html_be_strict) << '\n';
 
+       os << "\\docbook_table_output " << docbook_table_output << '\n';
+
        if (html_math_img_scale != 1.0)
                os << "\\html_math_img_scale " << convert<string>(html_math_img_scale) << '\n';
        if (!html_latex_start.empty())
index 4e176c945e7dddd0b031c2f9c971f0218dcec37c..b42d622fb38a39eb7366e6bc64230fd954a840fc 100644 (file)
@@ -558,6 +558,15 @@ public:
        std::string html_latex_end;
        ///
        bool html_css_as_file;
+
+       // do not change these values. we rely upon them.
+       enum TableOutput {
+               HTMLTable = 0,
+               CALSTable = 1
+       };
+       /// what format to use for table output in DocBook. present choices are above
+       TableOutput docbook_table_output;
+
        /// allow the LaTeX backend to run external programs
        bool shell_escape;
        /// generate output usable for reverse/forward search
index 508a961177cb899f2dd627c1eec946dc7db8d766..adca0947041da0107f4825ac3a3c4546630c749c 100644 (file)
@@ -925,6 +925,8 @@ GuiDocument::GuiDocument(GuiView & lv)
                this, SLOT(change_adaptor()));
        connect(outputModule->mathoutCB, SIGNAL(currentIndexChanged(int)),
                this, SLOT(change_adaptor()));
+       connect(outputModule->tableoutCB, SIGNAL(currentIndexChanged(int)),
+               this, SLOT(change_adaptor()));
 
        connect(outputModule->shellescapeCB, SIGNAL(stateChanged(int)),
                this, SLOT(shellescapeChanged()));
@@ -3718,6 +3720,13 @@ void GuiDocument::applyView()
        bp_.html_math_img_scale = outputModule->mathimgSB->value();
        bp_.display_pixel_ratio = theGuiApp()->pixelRatio();
 
+       int tablefmt = outputModule->tableoutCB->currentIndex();
+       if (tablefmt == -1)
+               tablefmt = 0;
+       BufferParams::TableOutput const to =
+                       static_cast<BufferParams::TableOutput>(tablefmt);
+       bp_.docbook_table_output = to;
+
        bp_.save_transient_properties =
                outputModule->saveTransientPropertiesCB->isChecked();
        bp_.postpone_fragile_content =
@@ -4355,6 +4364,8 @@ void GuiDocument::paramsToDialog()
        outputModule->strictCB->setChecked(bp_.html_be_strict);
        outputModule->cssCB->setChecked(bp_.html_css_as_file);
 
+       outputModule->tableoutCB->setCurrentIndex(bp_.docbook_table_output);
+
        outputModule->saveTransientPropertiesCB
                ->setChecked(bp_.save_transient_properties);
        outputModule->postponeFragileCB
index b828e2304a13c9d7f61ecac3bf13c675ecedba9b..6dc2a297edfae6ec0496367ca2a5683fdd11dca4 100644 (file)
    <string>Form</string>
   </property>
   <layout class="QGridLayout" name="gridLayout_2">
+   <item row="4" column="0">
+    <widget class="QGroupBox" name="docbookGB">
+     <property name="title">
+      <string>DocBook Output Options</string>
+     </property>
+     <property name="flat">
+      <bool>true</bool>
+     </property>
+     <layout class="QGridLayout" name="outDBGridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="tableoutLA">
+        <property name="font">
+         <font>
+          <weight>50</weight>
+          <bold>false</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>&amp;Table output:</string>
+        </property>
+        <property name="buddy">
+         <cstring>tableoutCB</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="tableoutCB">
+        <property name="font">
+         <font>
+          <weight>50</weight>
+          <bold>false</bold>
+         </font>
+        </property>
+        <property name="toolTip">
+         <string>Format to use for math output.</string>
+        </property>
+        <item>
+         <property name="text">
+          <string>HTML</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>CALS</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer_5">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="5" column="0">
+    <widget class="QGroupBox" name="savingGB">
+     <property name="title">
+      <string>LyX Format</string>
+     </property>
+     <property name="flat">
+      <bool>true</bool>
+     </property>
+     <property name="checkable">
+      <bool>false</bool>
+     </property>
+     <layout class="QGridLayout" name="gridLayout7">
+      <item row="0" column="0">
+       <widget class="QCheckBox" name="saveTransientPropertiesCB">
+        <property name="toolTip">
+         <string>Save all parameters in the LyX file, including ones that are frequently switched or that are specific to the user (such as the output of the tracked changes, or the document directory path). Disabling this option plays nicer in collaborative settings and with version control systems.</string>
+        </property>
+        <property name="text">
+         <string>Save &amp;transient properties</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
    <item row="0" column="0">
     <widget class="QGroupBox" name="outputFormatGB">
      <property name="title">
      </layout>
     </widget>
    </item>
-   <item row="1" column="0">
-    <widget class="QCheckBox" name="shellescapeCB">
-     <property name="toolTip">
-      <string>Runs the LaTeX backend with the -shell-escape option (Warning: use only when really necessary)</string>
-     </property>
-     <property name="text">
-      <string>&amp;Allow running external programs</string>
-     </property>
-    </widget>
-   </item>
    <item row="3" column="0">
     <widget class="QGroupBox" name="xhtmlGB">
      <property name="title">
          <string>&amp;Math output:</string>
         </property>
         <property name="buddy">
-         <cstring>mathoutCB</cstring>
+         <cstring>tableoutCB</cstring>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
-   <item row="4" column="0">
-    <widget class="QGroupBox" name="savingGB">
-     <property name="title">
-      <string>LyX Format</string>
-     </property>
-     <property name="flat">
-      <bool>true</bool>
+   <item row="1" column="0">
+    <widget class="QCheckBox" name="shellescapeCB">
+     <property name="toolTip">
+      <string>Runs the LaTeX backend with the -shell-escape option (Warning: use only when really necessary)</string>
      </property>
-     <property name="checkable">
-      <bool>false</bool>
+     <property name="text">
+      <string>&amp;Allow running external programs</string>
      </property>
-     <layout class="QGridLayout" name="gridLayout7">
-      <item row="0" column="0">
-       <widget class="QCheckBox" name="saveTransientPropertiesCB">
-        <property name="toolTip">
-         <string>Save all parameters in the LyX file, including ones that are frequently switched or that are specific to the user (such as the output of the tracked changes, or the document directory path). Disabling this option plays nicer in collaborative settings and with version control systems.</string>
-        </property>
-        <property name="text">
-         <string>Save &amp;transient properties</string>
-        </property>
-       </widget>
-      </item>
-     </layout>
     </widget>
    </item>
-   <item row="5" column="0">
-    <spacer name="verticalSpacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>40</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
    <item row="2" column="0">
     <widget class="QGroupBox" name="latexOutputGB">
      <property name="title">
      </layout>
     </widget>
    </item>
+   <item row="6" column="0">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
   </layout>
  </widget>
  <includes>
index 2c38e3a7d9b3c59df1459192e56e2b5b704ebb88..87988afa70f5173aa817ac4f7d60c12236ae171a 100644 (file)
@@ -3501,6 +3501,20 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const
 
 void Tabular::docbookRow(XMLStream & xs, row_type row,
                   OutputParams const & runparams, bool header) const
+{
+       switch (buffer().params().docbook_table_output) {
+       case BufferParams::HTMLTable:
+               docbookRowAsHTML(xs, row, runparams, header);
+               break;
+       case BufferParams::CALSTable:
+               docbookRowAsCALS(xs, row, runparams);
+               break;
+       }
+}
+
+
+void Tabular::docbookRowAsHTML(XMLStream & xs, row_type row,
+                  OutputParams const & runparams, bool header) const
 {
        string const celltag = header ? "th" : "td";
        idx_type cell = getFirstCellInRow(row);
@@ -3565,7 +3579,77 @@ void Tabular::docbookRow(XMLStream & xs, row_type row,
                ++cell;
        }
        xs << xml::EndTag("tr");
-       xs<< xml::CR();
+       xs << xml::CR();
+}
+
+
+void Tabular::docbookRowAsCALS(XMLStream & xs, row_type row,
+                                OutputParams const & runparams) const
+{
+       idx_type cell = getFirstCellInRow(row);
+
+       xs << xml::StartTag("row");
+       xs << xml::CR();
+       for (col_type c = 0; c < ncols(); ++c) {
+               if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
+                       continue;
+
+               stringstream attr;
+
+               attr << "align='";
+               switch (getAlignment(cell)) {
+               case LYX_ALIGN_BLOCK:
+                       attr << "justify";
+                       break;
+               case LYX_ALIGN_DECIMAL: {
+                       Language const *tlang = buffer().paragraphs().front().getParLanguage(buffer().params());
+                       attr << "char' char='" << to_utf8(tlang->decimalSeparator());
+               }
+                       break;
+               case LYX_ALIGN_LEFT:
+                       attr << "left";
+                       break;
+               case LYX_ALIGN_RIGHT:
+                       attr << "right";
+                       break;
+
+               default:
+                       attr << "center";
+                       break;
+               }
+               attr << "'";
+               attr << " valign='";
+               switch (getVAlignment(cell)) {
+               case LYX_VALIGN_TOP:
+                       attr << "top";
+                       break;
+               case LYX_VALIGN_BOTTOM:
+                       attr << "bottom";
+                       break;
+               case LYX_VALIGN_MIDDLE:
+                       attr << "middle";
+               }
+               attr << "'";
+
+               if (isMultiColumn(cell))
+                       attr << " colspan='" << columnSpan(cell) << "'";
+               else if (isMultiRow(cell))
+                       attr << " rowspan='" << rowSpan(cell) << "'";
+               else
+                       attr << " colname='c" << (c + 1) << "'"; // Column numbering starts at 1.
+
+               // All cases where there should be a line *below* this row.
+               if (row_info[row].bottom_space_default)
+                       attr << " rowsep='1'";
+
+               xs << xml::StartTag("entry", attr.str(), true);
+               cellInset(cell)->docbook(xs, runparams);
+               xs << xml::EndTag("entry");
+               xs << xml::CR();
+               ++cell;
+       }
+       xs << xml::EndTag("row");
+       xs << xml::CR();
 }
 
 
@@ -3580,35 +3664,57 @@ void Tabular::docbook(XMLStream & xs, OutputParams const & runparams) const
                xs << xml::CR();
        }
 
-       // "Formal" tables have a caption and use the tag <table>; the distinction with <informaltable> is done outside.
+       // "Formal" tables have a title and use the tag <table>; the distinction with <informaltable> is done outside.
+       // HTML has the caption first with titles forbidden, and CALS has a title first.
        if (haveLTCaption()) {
-               xs << xml::StartTag("caption");
+               std::string tag = ((buffer().params().docbook_table_output) == BufferParams::HTMLTable) ? "caption" : "title";
+
+               xs << xml::StartTag(tag);
                for (row_type r = 0; r < nrows(); ++r)
                        if (row_info[r].caption)
                                docbookRow(xs, r, runparams);
-               xs << xml::EndTag("caption");
+               xs << xml::EndTag(tag);
                xs << xml::CR();
        }
 
-       // output header info
+       // CALS header: describe all columns in this table. For names, take 'c' then the ID of the column.
+       // Start at one, as is customary with CALS!
+       if (buffer().params().docbook_table_output == BufferParams::CALSTable) {
+               for (col_type c = 0; c < ncols(); ++c) {
+                       std::stringstream attr;
+                       attr << "colnum='" << (c + 1) << "' ";
+                       attr << "colname='c" << (c + 1) << "' ";
+                       Length const cwidth = column_info[c].p_width;
+                       if (!cwidth.zero())
+                               attr << "colwidth='" << cwidth.asHTMLString() << "' ";
+                       attr << "rowheader='norowheader'"; // Last attribute, hence no space at the end.
+
+                       xs << xml::CompTag("colspec", attr.str());
+                       xs << xml::CR();
+               }
+       }
+
+       // Output the header of the table. For both HTML and CALS, this is surrounded by a thead.
        bool const havefirsthead = haveLTFirstHead(false);
        // if we have a first head, then we are going to ignore the
        // headers for the additional pages, since there aren't any
-       // in XHTML. this test accomplishes that.
+       // in DocBook. this test accomplishes that.
        bool const havehead = !havefirsthead && haveLTHead(false);
        if (havehead || havefirsthead) {
                xs << xml::StartTag("thead") << xml::CR();
                for (row_type r = 0; r < nrows(); ++r) {
                        if (((havefirsthead && row_info[r].endfirsthead) ||
-                                (havehead && row_info[r].endhead)) &&
-                               !row_info[r].caption) {
-                               docbookRow(xs, r, runparams, true);
+                            (havehead && row_info[r].endhead)) &&
+                           !row_info[r].caption) {
+                               docbookRow(xs, r, runparams, true); // TODO: HTML vs CALS
                        }
                }
                xs << xml::EndTag("thead");
                xs << xml::CR();
        }
-       // output footer info
+
+       // Output the footer of the table. For both HTML and CALS, this is surrounded by a tfoot and output just after
+       // the header (and before the body).
        bool const havelastfoot = haveLTLastFoot(false);
        // as before.
        bool const havefoot = !havelastfoot && haveLTFoot(false);
@@ -3616,15 +3722,17 @@ void Tabular::docbook(XMLStream & xs, OutputParams const & runparams) const
                xs << xml::StartTag("tfoot") << xml::CR();
                for (row_type r = 0; r < nrows(); ++r) {
                        if (((havelastfoot && row_info[r].endlastfoot) ||
-                                (havefoot && row_info[r].endfoot)) &&
-                               !row_info[r].caption) {
-                               docbookRow(xs, r, runparams);
+                            (havefoot && row_info[r].endfoot)) &&
+                           !row_info[r].caption) {
+                               docbookRow(xs, r, runparams); // TODO: HTML vs CALS
                        }
                }
                xs << xml::EndTag("tfoot");
                xs << xml::CR();
        }
 
+       // Output the main part of the table. The tbody container is mandatory for CALS, but optional for HTML (only if
+       // there is no header and no footer). It never hurts to have it, though.
        xs << xml::StartTag("tbody");
        xs << xml::CR();
        for (row_type r = 0; r < nrows(); ++r)
@@ -3633,6 +3741,7 @@ void Tabular::docbook(XMLStream & xs, OutputParams const & runparams) const
        xs << xml::EndTag("tbody");
        xs << xml::CR();
 
+       // If this method started the table tag, also make it close it.
        if (!hasTableStarted) {
                xs << xml::EndTag("informaltable");
                xs << xml::CR();
index 4166f316c3273c3f13b6d0c4de0a410c95bd7171..de26070b7b4af4727114666c85948f183883219d 100644 (file)
@@ -572,7 +572,7 @@ public:
        void read(Lexer &);
        ///
        void latex(otexstream &, OutputParams const &) const;
-       ///
+       /// serialise the table in DocBook, according to buffer parameters
        void docbook(XMLStream &, OutputParams const &) const;
        ///
        docstring xhtml(XMLStream &, OutputParams const &) const;
@@ -907,9 +907,14 @@ public:
                                idx_type cell, row_type row, col_type column,
                                std::vector<unsigned int> const &,
                                bool onlydata, size_t max_length) const;
-       /// auxiliary function for docbook
+       /// auxiliary function for DocBook
        void docbookRow(XMLStream &, row_type, OutputParams const &,
                                        bool header = false) const;
+       /// auxiliary function for DocBook: export this row as HTML
+       void docbookRowAsHTML(XMLStream &, row_type, OutputParams const &,
+                                       bool header) const;
+       /// auxiliary function for DocBook: export this row as CALS
+       void docbookRowAsCALS(XMLStream &, row_type, OutputParams const &) const;
        ///
        docstring xhtmlRow(XMLStream & xs, row_type, OutputParams const &,
                           bool header = false) const;
index 005a10348690f7145badf0b874a61724b23b9671..737ac6d316cb39f18a4db5e24200b08ad6b21200 100644 (file)
@@ -543,6 +543,7 @@ Preamble::Preamble() : one_language(true), explicit_babel(false),
        h_html_be_strict          = "false";
        h_html_css_as_file        = "0";
        h_html_math_output        = "0";
+       h_docbook_table_output    = "0";
        h_index[0]                = "Index";
        h_index_command           = "default";
        h_inputencoding           = "auto-legacy";
@@ -2117,6 +2118,7 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled
           << "\\html_math_output " << h_html_math_output << "\n"
           << "\\html_css_as_file " << h_html_css_as_file << "\n"
           << "\\html_be_strict " << h_html_be_strict << "\n"
+          << "\\docbook_table_output " << h_docbook_table_output << "\n"
           << authors_
           << "\\end_header\n\n"
           << "\\begin_body\n";
index 42c6069fd24ca4609ccfcd46271ae5b7ecab52a5..dccd780a4eee6621d4ee3d9db5fbb69239f2a72b 100644 (file)
@@ -190,6 +190,7 @@ private:
        std::string h_html_be_strict;
        std::string h_html_css_as_file;
        std::string h_html_math_output;
+       std::string h_docbook_table_output;
        std::string h_index[99];
        std::string h_index_command;
        std::string h_inputencoding;
index b6e5600cf5562d5f0e1be18b4ff4190183a20c11..770c3083624e809b84c53ac5a57e0f6d151f8a6b 100644 (file)
@@ -32,8 +32,8 @@ extern char const * const lyx_version_info;
 
 // Do not remove the comment below, so we get merge conflict in
 // independent branches. Instead add your own.
-#define LYX_FORMAT_LYX 597 // spitz: libertinus fonts
-#define LYX_FORMAT_TEX2LYX 597
+#define LYX_FORMAT_LYX 598 // tcuvelier: DocBook tables
+#define LYX_FORMAT_TEX2LYX 598
 
 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX
 #ifndef _MSC_VER