]> git.lyx.org Git - lyx.git/commitdiff
XHTML tables: fix borders and implement booktabs.
authorThibaut Cuvelier <tcuvelier@lyx.org>
Fri, 1 Apr 2022 19:15:13 +0000 (21:15 +0200)
committerThibaut Cuvelier <tcuvelier@lyx.org>
Sat, 2 Apr 2022 01:34:33 +0000 (03:34 +0200)
https://www.lyx.org/trac/ticket/10154

Contributed by raccoon.

autotests/export/xhtml/table_borders.lyx [new file with mode: 0644]
autotests/export/xhtml/table_borders.xhtml [new file with mode: 0644]
lib/layouts/stdinsets.inc
src/insets/InsetTabular.cpp
src/insets/InsetTabular.h

diff --git a/autotests/export/xhtml/table_borders.lyx b/autotests/export/xhtml/table_borders.lyx
new file mode 100644 (file)
index 0000000..7b59f5f
--- /dev/null
@@ -0,0 +1,235 @@
+#LyX 2.4 created this file. For more info see https://www.lyx.org/
+\lyxformat 609
+\begin_document
+\begin_header
+\save_transient_properties true
+\origin unavailable
+\textclass scrbook
+\begin_preamble
+% Added by lyx2lyx
+\setlength{\parskip}{\medskipamount}
+\setlength{\parindent}{0pt}
+\end_preamble
+\use_default_options true
+\maintain_unincluded_children no
+\language ngerman
+\language_package default
+\inputencoding auto-legacy
+\fontencoding auto
+\font_roman "lmodern" "default"
+\font_sans "lmss" "default"
+\font_typewriter "lmtt" "default"
+\font_math "auto" "auto"
+\font_default_family default
+\use_non_tex_fonts false
+\font_sc false
+\font_roman_osf false
+\font_sans_osf false
+\font_typewriter_osf false
+\font_sf_scale 100 100
+\font_tt_scale 100 100
+\use_microtype false
+\use_dash_ligatures true
+\graphics default
+\default_output_format default
+\output_sync 0
+\bibtex_command default
+\index_command default
+\paperfontsize default
+\spacing single
+\use_hyperref false
+\papersize default
+\use_geometry false
+\use_package amsmath 1
+\use_package amssymb 1
+\use_package cancel 1
+\use_package esint 1
+\use_package mathdots 0
+\use_package mathtools 0
+\use_package mhchem 1
+\use_package stackrel 1
+\use_package stmaryrd 1
+\use_package undertilde 0
+\cite_engine basic
+\cite_engine_type default
+\biblio_style plain
+\use_bibtopic false
+\use_indices false
+\paperorientation portrait
+\suppress_date false
+\justification true
+\use_refstyle 0
+\use_minted 0
+\use_lineno 0
+\branch Test
+\selected 1
+\filename_suffix 0
+\color #e9c792 #16386d
+\end_branch
+\index Stichwortverzeichnis
+\shortcut idx
+\color #008000
+\end_index
+\secnumdepth 3
+\tocdepth 3
+\paragraph_separation indent
+\paragraph_indentation default
+\is_math_indent 0
+\math_numbering_side default
+\quotes_style english
+\dynamic_quotes 0
+\papercolumns 1
+\papersides 1
+\paperpagestyle default
+\tablestyle default
+\tracking_changes false
+\output_changes false
+\change_bars false
+\postpone_fragile_content false
+\html_math_output 0
+\html_css_as_file 0
+\html_be_strict false
+\docbook_table_output 0
+\docbook_mathml_prefix 1
+\html_latex_start <span class='latex'>
+\html_latex_end </span>
+\end_header
+
+\begin_body
+
+\begin_layout Title
+Check HTML export
+\end_layout
+
+\begin_layout Standard
+\begin_inset Tabular
+<lyxtabular version="3" rows="4" columns="3">
+<features tabularvalignment="middle">
+<column alignment="center" valignment="top" width="3cm">
+<column alignment="center" valignment="top">
+<column alignment="center" valignment="top">
+<row>
+<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+a
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+d
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+s
+\end_layout
+
+\end_inset
+</cell>
+</row>
+<row>
+<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+</row>
+<row>
+<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" leftline="true" rightline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+</row>
+<row>
+<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+<cell alignment="center" valignment="top" topline="true" bottomline="true" leftline="true" rightline="true" usebox="none">
+\begin_inset Text
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+</cell>
+</row>
+</lyxtabular>
+
+\end_inset
+
+
+\end_layout
+
+\end_body
+\end_document
diff --git a/autotests/export/xhtml/table_borders.xhtml b/autotests/export/xhtml/table_borders.xhtml
new file mode 100644 (file)
index 0000000..7e0602e
--- /dev/null
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="GENERATOR" content="LyX 2.4.0dev" />
+<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
+<title>Check HTML export</title>
+<style type='text/css'>
+/* Layout-provided Styles */
+h1.title {
+font-family: sans-serif;
+font-weight: bold;
+font-size: x-large;
+margin-bottom: 1ex;
+text-align: center;
+
+}
+h1.chapter {
+font-family: sans-serif;
+font-weight: bold;
+font-size: x-large;
+margin-top: 2ex;
+margin-bottom: 0.8ex;
+text-align: left;
+
+}
+div.standard {
+       text-indent: 2em;
+       margin-bottom: 2ex;
+}
+div.plain_layout {
+text-align: left;
+
+}
+table {
+       border-collapse: collapse;
+       display: inline-block;
+}
+td {
+       padding: 0.5ex;
+}
+
+
+</style>
+</head>
+<body dir="auto">
+<h1 class="title" id='magicparlabel-1'>Check HTML export</h1>
+<section>
+<h1 class="chapter" id='magicparlabel-2'><span class="chapter_label">1</span> here<a id="sec_ere__dsd" /></h1>
+<div class="standard" id='magicparlabel-3'>see <a href="#sec_ere__dsd">1</a></div>
+
+<div class="standard" id='magicparlabel-4'><table>
+<tbody>
+<tr>
+<td style='width: 3cm; border-bottom: 3.000000px double; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'><div class="plain_layout" id='magicparlabel-26'>a</div>
+</td>
+<td style='border-bottom: 3.000000px double; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'><div class="plain_layout" id='magicparlabel-29'>d</div>
+</td>
+<td style='border-bottom: 3.000000px double; border-right: 1px solid; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'><div class="plain_layout" id='magicparlabel-32'>s</div>
+</td>
+</tr>
+<tr>
+<td style='width: 3cm; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+<td style='border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+<td style='border-right: 1px solid; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+</tr>
+<tr>
+<td style='width: 3cm; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+<td style='border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+<td style='border-right: 1px solid; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+</tr>
+<tr>
+<td style='width: 3cm; border-bottom: 1.000000px solid; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+<td style='border-bottom: 1.000000px solid; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+<td style='border-bottom: 1.000000px solid; border-right: 1px solid; border-left: 1px solid; border-top: 1.000000px solid' align='center' valign='top'>
+</td>
+</tr>
+</tbody>
+</table>
+</div>
+</section>
+</body>
+</html>
index 32a0af3996f118e95b2663e61291f85a90d98343..074b2748d52b11ad441b554a195e007d7416d22c 100644 (file)
@@ -776,7 +776,6 @@ InsetLayout Tabular
                        display: inline-block;
                }
                td {
-                       border: 1px solid black;
                        padding: 0.5ex;
                }
        EndHTMLStyle
index 0832054bc3ddf9b8657eb315d0fba815845dbfb5..478ecf1fa665d6ddc194d238e9d9a47824b08d42 100644 (file)
@@ -3654,6 +3654,77 @@ std::string Tabular::getHAlignAsXmlAttribute(idx_type cell, bool is_xhtml) const
        }
 }
 
+Tabular::XmlRowWiseBorders Tabular::computeXmlBorders(row_type row) const
+{
+       Tabular::XmlRowWiseBorders borders;
+
+       // Determine whether borders are required.
+       for (col_type c = 0; c < ncols(); ++c) {
+               if (row < nrows() - 1) {
+                       if (!bottomLine(cellIndex(row, c))
+                           || !topLine(cellIndex(row + 1, c))) {
+                               borders.completeBorder = false;
+                       }
+                       if (!bottomLine(cellIndex(row, c))
+                           && !topLine(cellIndex(row + 1, c))) {
+                               borders.completeBorderBelow = false;
+                       }
+               } else if (row == nrows() - 1 && !bottomLine(cellIndex(row, c))) {
+                       borders.completeBorderBelow = false;
+               }
+
+               if ((row > 0 && !bottomLine(cellIndex(row - 1, c)) && !topLine(cellIndex(row, c))) ||
+                   (row == 0 && !topLine(cellIndex(row, c)))) {
+                       borders.completeBorderAbove = false;
+               }
+       }
+
+       // Size of booktabs borders.
+       if (use_booktabs) {
+               if (borders.completeBorderAbove)
+                       borders.borderTopWidth = row == 0 ? 2 : 1.5;
+               if (borders.completeBorderBelow) {
+                       borders.borderBottomWidth = row == nrows() - 1 ? 2 : 1.5;
+                       borders.borderBottomWidthComplete = 3 * borders.borderBottomWidth;
+               }
+       }
+
+       return borders;
+}
+
+
+std::vector<std::string> Tabular::computeCssStylePerCell(row_type row, col_type col, idx_type cell) const
+{
+       std::vector<std::string> styles;
+
+       // Fixed width.
+       Length const col_width = column_info[col].p_width;
+       if (!col_width.zero())
+               styles.emplace_back("width: " + col_width.asHTMLString());
+
+       // Borders and booktabs.
+       const Tabular::XmlRowWiseBorders borders = computeXmlBorders(row);
+
+       if (bottomLine(cell)) {
+               if (row < nrows() - 1 && borders.completeBorder)
+                       styles.emplace_back("border-bottom: " + to_string(borders.borderBottomWidthComplete) + "px double");
+               else
+                       styles.emplace_back("border-bottom: " + to_string(borders.borderBottomWidth) + "px solid");
+       }
+       if (rightLine(cell)) {
+               if (col < ncols() - 1 && leftLine(cell + 1))
+                       styles.emplace_back("border-right: 3px double");
+               else
+                       styles.emplace_back("border-right: 1px solid");
+       }
+       if (leftLine(cell))
+               styles.emplace_back("border-left: 1px solid");
+       if (topLine(cell))
+               styles.emplace_back("border-top: " + to_string(borders.borderTopWidth) + "px solid");
+
+       return styles;
+}
+
 
 docstring Tabular::xmlRow(XMLStream & xs, row_type row, OutputParams const & runparams,
        bool header, bool is_xhtml, BufferParams::TableOutput docbook_table_output) const
@@ -3663,22 +3734,26 @@ docstring Tabular::xmlRow(XMLStream & xs, row_type row, OutputParams const & run
 
        std::string const row_tag = is_xhtml_table ? "tr" : "row";
        std::string const cell_tag = is_xhtml_table ? (header ? "th" : "td") : "entry";
+       Tabular::XmlRowWiseBorders const borders = computeXmlBorders(row);
        idx_type cell = getFirstCellInRow(row);
 
        xs << xml::StartTag(row_tag);
        xs << xml::CR();
-       for (col_type c = 0; c < ncols(); ++c) {
+       for (col_type c = 0; c < ncols(); ++c, ++cell) {
                if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
                        continue;
 
                stringstream attr;
 
                if (is_xhtml_table) {
-                       Length const cwidth = column_info[c].p_width;
-                       if (!cwidth.zero()) {
-                               string const hwidth = cwidth.asHTMLString();
-                               attr << "style='width: " << hwidth << ";' ";
-                       }
+                       const std::vector<std::string> styles = computeCssStylePerCell(row, c, cell);
+                       attr << "style='" ;
+               for (auto it = styles.begin(); it != styles.end(); ++it) {
+                               attr << *it;
+                               if (it != styles.end() - 1)
+                                       attr << "; ";
+               }
+                       attr << "' ";
                }
 
                attr << getHAlignAsXmlAttribute(cell, false) << " " << getVAlignAsXmlAttribute(cell);
@@ -3707,7 +3782,6 @@ docstring Tabular::xmlRow(XMLStream & xs, row_type row, OutputParams const & run
                }
                xs << xml::EndTag(cell_tag);
                xs << xml::CR();
-               ++cell;
        }
        xs << xml::EndTag(row_tag);
        xs << xml::CR();
index 8a99ef40865fc1e489b332265225e26e2f568036..0fbafa8ede673170f8d79820563e0b533abb0d2e 100644 (file)
@@ -850,6 +850,24 @@ public:
        ///
        typedef std::vector<ColumnData> column_vector;
 
+private:
+       // Determines the style of borders, per row.
+       class XmlRowWiseBorders {
+       public:
+               // Whether to draw double bottom line.
+               bool completeBorder = true;
+
+               // Whether to draw booktabs' thicker lines.
+               bool completeBorderAbove = true;
+               bool completeBorderBelow = true;
+
+               // Size of the borders.
+               double borderBottomWidth = 1.0;
+               double borderBottomWidthComplete = 3.0;
+               double borderTopWidth = 1.0;
+       };
+
+public:
        ///
        idx_type numberofcells;
        ///
@@ -939,6 +957,8 @@ public:
        docstring xmlRow(XMLStream & xs, row_type row, OutputParams const &,
                         bool header = false, bool is_xhtml = true,
                                         BufferParams::TableOutput docbook_table_output = BufferParams::TableOutput::HTMLTable) const;
+       XmlRowWiseBorders computeXmlBorders(row_type row) const;
+       std::vector<std::string> computeCssStylePerCell(row_type row, col_type col, idx_type cell) const;
 
        /// Transforms the vertical alignment of the given cell as a prebaked XML attribute (for HTML and CALS).
        std::string getHAlignAsXmlAttribute(idx_type cell, bool is_xhtml = true) const;