diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-22 22:20:48 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-22 22:20:48 +0000 |
commit | 1f8a99f91f89d808a685fb90434693017786a8a6 (patch) | |
tree | c0adb02e7b8fd39fc9335d04d8dcd020fb37b570 /Master/texmf-dist/source/latex/cellprops | |
parent | 4220324e99226b427519e0380034f951f67a011f (diff) | |
parent | d9e54ef03db8d244f5386fc89efffccb3d483a5e (diff) |
sync with trunk 57841 to continue testing
git-svn-id: svn://tug.org/texlive/branches/branch2020@57848 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/cellprops')
-rw-r--r-- | Master/texmf-dist/source/latex/cellprops/cellprops.dtx | 881 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/cellprops/cellprops.ins | 2 |
2 files changed, 667 insertions, 216 deletions
diff --git a/Master/texmf-dist/source/latex/cellprops/cellprops.dtx b/Master/texmf-dist/source/latex/cellprops/cellprops.dtx index 67b4dc8cd2f..264361e91e4 100644 --- a/Master/texmf-dist/source/latex/cellprops/cellprops.dtx +++ b/Master/texmf-dist/source/latex/cellprops/cellprops.dtx @@ -1,7 +1,7 @@ % \iffalse meta-comment % vim: tw=80 spl=en % -%% File: cellprops.dtx (C) Copyright 2016-2019 RIVAUD Julien +%% File: cellprops.dtx (C) Copyright 2016-2021 RIVAUD Julien %% %% It may be distributed and/or modified under the conditions of the %% General Public License (GPL), either version 3 of this @@ -14,8 +14,8 @@ \RequirePackage{expl3}[2018/06/19] \def\ExplFileName{cellprops} \def\ExplFileDescription{CSS-like cell and table properties} -\def\ExplFileDate{2019/09/29} -\def\ExplFileVersion{1.6} +\def\ExplFileDate{2021/01/30} +\def\ExplFileVersion{2.0} %</driver|package> %<*driver> \documentclass[full]{l3doc} @@ -66,13 +66,13 @@ % % I recommend to add globally: %\begin{verbatim} -%\cellprops{ td { padding: 1ex; min-height: 0pt; min-depth: 0pt; } } +% \cellprops{ td { padding: 1ex; min-height: 0pt; min-depth: 0pt; } } %\end{verbatim} % so that you get better-looking tables by default. % % \subsection{Examples} % -% To produce: +% To produce the (arguably ugly): % \[ % \cellprops{ % td { @@ -102,6 +102,15 @@ % math-mode: text; % text-align: left; % } +% tr:nth-child(6) td:nth-child(1) { +% vertical-align: top; % see remark in usage guide +% } +% tr:nth-child(6) td:nth-child(2) { +% vertical-align: middle; +% } +% tr:nth-child(6) td:nth-child(3) { +% vertical-align: bottom; % see remark in usage guide +% } % } % \begin{array}{nnnp{5em}} % This is text & A_2 & A_3 & A_4 \\ @@ -109,6 +118,16 @@ % C1 & C_2 & X & Y \\ % D1 & D_2 & DX & v \\ % & F & \int_a^b f(t) dt & v \\ +% \fbox{Top} & \fbox{Middle} & \fbox{Bottom} & \fbox{Baseline}% +% \textcolor{red}{% +% \kern -9cm\relax +% \vrule height 0.1pt depth 0.1pt width 10cm +% \kern -1cm\relax +% }\textcolor{blue}{% +% \kern -9cm\relax +% \vrule height 0.52ex depth -0.48ex width 10cm +% \kern -1cm\relax +% } \\ % \end{array} % \] % you can use: @@ -142,13 +161,32 @@ % math-mode: text; % text-align: left; % } +% tr:nth-child(6) td:nth-child(1) { +% vertical-align: top; % see remark in usage guide +% } +% tr:nth-child(6) td:nth-child(2) { +% vertical-align: middle; +% } +% tr:nth-child(6) td:nth-child(3) { +% vertical-align: bottom; % see remark in usage guide +% } % } -% \begin{array}{nnnn} +% \begin{array}{nnnp{5em}} % This is text & A_2 & A_3 & A_4 \\ % B1 & This is maths & B_3 & \\ % C1 & C_2 & X & Y \\ % D1 & D_2 & DX & v \\ -% E & F & \int_a^b f(t) dt & v \\ +% & F & \int_a^b f(t) dt & v \\ +% \fbox{Top} & \fbox{Middle} & \fbox{Bottom} & \fbox{Baseline}% +% \textcolor{red}{% +% \kern -9cm\relax +% \vrule height 0.1pt depth 0.1pt width 10cm +% \kern -1cm\relax +% }\textcolor{blue}{% +% \kern -9cm\relax +% \vrule height 0.52ex depth -0.48ex width 10cm +% \kern -1cm\relax +% } \\ % \end{array} % \] %\end{verbatim} @@ -159,6 +197,8 @@ % %\cellprops{ % td { border: thin solid black; } +% tr:nth-child(-n+3) { background-color: black!10; } +% tr:nth-child(n+8) { background-color: blue!10; } % tr:nth-child(4n) td:first-child, % tr:nth-child(4n+1) td:nth-child(2), % tr:nth-child(4n+2) td:nth-child(3), @@ -198,20 +238,22 @@ % % This table has been produced by: %\begin{verbatim} -%\cellprops{ +% \cellprops{ % td { border: thin solid black; } +% tr:nth-child(-n+3) { background-color: black!10; } +% tr:nth-child(n+8) { background-color: blue!10; } % tr:nth-child(4n) td:first-child, % tr:nth-child(4n+1) td:nth-child(2), % tr:nth-child(4n+2) td:nth-child(3), % tr:nth-child(4n+3) td:nth-child(4) { % border: thick solid red; % } -%} -%\begin{longtable}{nnnn} +% } +% \begin{longtable}{nnnn} % aaaaa & baaaa & caaaaa & dbbbb \\ % ... % aaaaa & baaaa & caaaaa & dbbbb \\ -%\end{longtable} +% \end{longtable} %\end{verbatim} % % \subsection{Usage guide} @@ -225,15 +267,16 @@ % ] % \item[usage] "'\cellprops{'" [ \<selectors> "'{'" \<properties> "'}'" ]* "'}'" % \item[selectors] \<selector> ["," \<selectors> ] -% \item[selector] [\<environment> "' '"] \<element1> -% \item[element1] -% "'table'" \OR -% "'tr'"[\<pseudo-class>] ["' '" \<element2>] \OR -% \<element2> -% \item[element2] "'td'"[\<pseudo-class>] ["' '" \<parbox>] \OR \<parbox> -% \item[parbox] "'p'" -% \item[pseudo-class] "':nth-child('"\<nth>"')" -% \item[nth] \<number> \OR "'odd'" \OR "'even'" \OR \<number>"'n+'"\<number> +% \item[selector] [\<table> "' '"] [\<tr> "' '"] [\<td> "' '"] [\<parbox>] +% \item[table] "'table'" ["'.'" \<class>]* ["':where(.'"\<class>"')'"]* +% \OR "':where('"\<table>"')'" +% \item[tr] "'tr'" [\<pseudoclass>]* \OR "':where('"\<tr>"')'" +% \item[td] "'td'" [\<pseudoclass>]* \OR "':where('"\<td>"')'" +% \item[parbox] "'p'" \OR "':where('"\<p>"')'" +% \item[pseudoclass] "':where('"[\<pseudoclass>]*"')'" \OR "':nth-child('"\<nth>"')" +% \item[nth] +% "'odd'" \OR "'even'" \OR \<number> \OR +% \<number>"'n+'"\<number> \OR \<number>"'n-'"\<number> % \item[properties] [ \<property> "';'" ]* % \item[property] % "'padding: '" ( \<dimension> ) \{1,4\} \OR \\ @@ -245,6 +288,7 @@ % "'min-depth: '" \<dimension> \OR \\ % "'min-width: '" \<dimension> \OR \\ % "'text-align: '" ( "'left'" \OR "'right'" \OR "'center'" ) \OR \\ +% "'vertical-align: '" ( "'top'" \OR "'middle'" \OR "'baseline'" \OR "'bottom'" ) \OR \\ % "'math-mode: '" ( "'text'" \OR "'math'" \OR "'auto'" ) \OR \\ % "'color: '" \<color> \OR \\ % "'background-color: '" ( \<color> \OR "'transparent'" ) \OR \\ @@ -312,16 +356,36 @@ % "td:nth-child()" to select a column. Currently, cells spanning several % columns actually increase the child count by the number of column they % span, so that nth-child can still be used to select columns. -% This is not consistent with the CSS specification. -% \item Any ":nth-child("$A$"n+"$B$")" or ":nth-child("$A$"n)" -% or ":nth-child("$B$")" is supported, with arbitrary $A$~and~$B$. The -% performance will slowly degrade the more different~$A$ are active -% (but if in a \TeX\ group, they will become inactive again when leaving -% the group). \emph{A big difference} with the CSS specification is that -% currently $n$ is not enforced non-negative. In particular -% ":nth-child(2n+8)" \emph{will} match for the second element. This also -% prevents tricks like ":nth-child(-n+3)". I am investigating how to -% handle those without slowing down the general case. +% This is not consistent with the HTML specification of tables, but acts +% as if a cell spanning multiple columns was implicitly creating +% "display: none" empty cell siblings following it. +% \item Any ":nth-child("$A$"n+"$B$")" or ":nth-child("$A$"n)" or +% ":nth-child("$B$")" is supported, with arbitrary $A$~and~$B$, including +% nothing for~$A$ (standing for~$A = 1$) or just a minus sign (standing +% for~$A = -1$). +% \item "vertical-align" values "baseline" and "middle" are following the CSS +% specification. On the other hand, "top" (resp.~"bottom") align the top +% (resp.~bottom) of the cell with the row baseline which gives a +% different result than CSS if there is mixed alignment in the row. +% The common case where all cells have "vertical-align: top" or +% "vertical-align: bottom" behaves as expected from the CSS +% specification. +% +% As fas as I can tell, obeying CSS in all cases would require +% typesetting each row in two passes. +% \item Class names are matched against the classes defined with +% \cs{cellpropsclass}, which takes a space-separated list of classes. By +% default, \cs{cellpropsclass} also add as a class the name of the +% environment that created the tabular-like structure (that is, "tabular" +% or "array" or "matrix" or similar). If you do not want that behavior, +% you can use \cs{cellpropsclass*}. The class list is +% redefined locally, and \pkg{cellprops} initially calls +% "\cellpropsclass{}". +% \item The ":where()" pseudo-class is the same as in CSS selectors level 4, +% except that it does not accept spaces nor commas. Its only effect is to +% neutering the specificity of the selectors within while still keeping +% their matching conditions. This is mainly useful to write default yet +% specific rules whose specificity would otherwise dwarf simple rules. % \end{itemize} % % \subsection{Compatibility} @@ -338,8 +402,7 @@ % % Add a test suite with compatibility tests. Improve % the documentation, and test more \LaTeX\ table constructs and preamble column -% types. Enforce $n\ge0$ in ":nth-child" selectors to match the CSS -% specification. +% types. % % \end{documentation} % @@ -452,6 +515,12 @@ % % \end{macro} % +% \begin{macro}{\@@_parse_properties:nn} +% Now we can parse the block of properties for a given selector. The first +% argument is the token list variable which will ultimately hold the expanded +% code setting internal variables from the properties. That code will be +% called when recalling the computed values in a specific context. +% % \begin{macrocode} \cs_new_protected:Nn \@@_parse_properties:Nn { \tl_clear:N #1 @@ -459,25 +528,34 @@ \seq_map_inline:Nn \l_tmpa_seq { \tl_if_empty:nF {##1} { \exp_args:NNV \seq_set_split:Nnn \l_tmpb_seq \c_colon_str {##1} - \int_compare:nNnTF {\seq_count:N \l_tmpb_seq} = { 2 } { + \int_compare:nNnT {\seq_count:N \l_tmpb_seq} = { 2 } { \seq_get_left:NN \l_tmpb_seq \l_tmpa_tl \exp_args:NNV \str_set:Nn \l_tmpa_str \l_tmpa_tl \seq_get_right:NN \l_tmpb_seq \l_tmpa_tl - \cs_if_exist:cTF { _@@_property_type_\l_tmpa_str :nn } { + \cs_if_exist:cT { _@@_property_type_\l_tmpa_str :nn } { \tl_put_right:Nx #1 { \exp_args:NVV \@@_delegate_setter:nn \l_tmpa_str \l_tmpa_tl } - }{ - % TODO: ERROR-no property with that name } - }{ - % TODO: ERROR-too many : or none at all } } } } - +% \end{macrocode} +% \end{macro} +% +% \subsection{Defining new properties} +% +% \subsubsection{Some helpers} +% +% \begin{macro}{\@@_fourval_setter:nnnnnn} +% \begin{macro}{\@@_define_fourval_properties:nnnnnn} +% We first define helpers to parse and define compound properties like +% "padding" where you can give one to four different values and the missing +% values are copied from the given ones. +% +% \begin{macrocode} \cs_new:Nn \@@_fourval_setter:nnnnnn { \@@_fourval_setter_aux:w {#1}{#2}{#3}{#4}#6~{\q_no_value}~{\q_no_value}~{\q_no_value}~\q_stop @@ -510,7 +588,14 @@ #2 } } - +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_color_setter:nn} +% This macro is used to parse color definitions, either named, rgb, or hsl. +% +% \begin{macrocode} \tl_const:Nn \c_@@_inherit_color_tl { \q_nil } \cs_new_nopar:Nn \@@_color_setter:nn { @@ -533,6 +618,13 @@ } } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_bgcolor_setter:nn} +% For background colors, we support "transparent" as an alias for "inherit". +% +% \begin{macrocode} \cs_new_nopar:Nn \@@_bgcolor_setter:nn { \str_if_eq:nnTF {#2} {transparent} { \@@_color_setter:nn {#1} {inherit} @@ -540,7 +632,13 @@ \@@_color_setter:nn {#1} {#2} } } - +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_linewidth_setter:nn} +% A setter for line widths that supports common keywords: +% +% \begin{macrocode} \cs_new_nopar:Nn \@@_linewidth_setter:nn { \str_case:nnF {#2} { {thin} { \@@_generic_setter:nnn \exp_not:n {#1} { \fboxrule} } @@ -550,7 +648,16 @@ \@@_generic_setter:nnn \exp_not:n {#1} {#2} } } - +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_border_setter:nn } +% "border" and "border-"<side> are compound properties that can define the +% width, the style and the color. As per the specification, the "border" +% property always sets all four sides at the same time instead of being a +% four-valued property. +% +% \begin{macrocode} \cs_new_nopar:Nn \@@_border_setter:nn { \@@_border_setter_aux:nw {#1}#2~{\q_no_value}~{\q_no_value}~\q_stop @@ -567,7 +674,7 @@ } }{ \quark_if_no_value:nTF {#3} { - %% TODO: Error, one no-style value, ambiguous + %% One no-style value, ambiguous. Ignore the property }{ \@@_border_setter_isstyle:nTF {#3} { \@@_delegate_setter:nn {#1-width} {#2} @@ -586,13 +693,22 @@ \@@_delegate_setter:nn {#1-color} {#4} } } + \cs_new:Npn \@@_border_setter_isstyle:nTF #1 { \str_case:nnTF {#1} { {none}{} {hidden}{} {dotted}{} {dashed}{} {solid}{} {double}{} {groove}{} {ridge}{} {inset}{} {outset}{} } } - +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Actual definitions of properties} +% +% First some simple-valued properties where we just store the value +% unexpanded. +% +% \begin{macrocode} \@@_define_properties:nn { \@@_generic_setter:nnn \exp_not:n }{ @@ -600,19 +716,33 @@ min-depth, min-width, } - +% \end{macrocode} +% +% "padding" is a compound property for "padding-"<side> which store their +% value unexpanded. +% +% \begin{macrocode} \@@_define_fourval_properties:nnnnnn { \@@_generic_setter:nnn \exp_not:n } {padding} {padding-top}{padding-right}{padding-bottom}{padding-left} - +% \end{macrocode} +% +% Simple-valued properties that store a str value. +% +% \begin{macrocode} \@@_define_properties:nn { \@@_generic_setter:nnn \tl_to_str:n }{ text-align, + vertical-align, math-mode, } - +% \end{macrocode} +% +% Some simple-valued color properties, using the dedicated parser. +% +% \begin{macrocode} \@@_define_properties:nn { \@@_color_setter:nn }{ @@ -624,167 +754,464 @@ }{ background-color, } - +% \end{macrocode} +% +% A compound property whose individual sides use the linewidth setter for +% keyword recognition. +% +% \begin{macrocode} \@@_define_fourval_properties:nnnnnn { \@@_linewidth_setter:nn } {border-width} {border-top-width}{border-right-width} {border-bottom-width}{border-left-width} - +% \end{macrocode} +% +% A compound property whose individual sides are str values. They could be +% checked against the list of valid values, but any non-existing one will be +% ignored anyway due to the way they are implemented. +% +% \begin{macrocode} \@@_define_fourval_properties:nnnnnn { \@@_generic_setter:nnn \tl_to_str:n } {border-style} {border-top-style}{border-right-style} {border-bottom-style}{border-left-style} - +% \end{macrocode} +% +% A compound property whose individual sides are colors. +% +% \begin{macrocode} \@@_define_fourval_properties:nnnnnn { \@@_color_setter:nn } {border-color} {border-top-color}{border-right-color} {border-bottom-color}{border-left-color} - +% \end{macrocode} +% +% The five border-specific compound properties are defined here. +% +% \begin{macrocode} \@@_define_properties:nn { \@@_border_setter:nn }{ border, border-top, border-right, border-bottom, border-left } - +% \end{macrocode} +% +% \subsection{Parsing a CSS stylesheet} +% +% \begin{macrocode} \NewDocumentCommand \cellprops { m } { \@@_parse_css:n {#1} } \cs_new_protected:Nn \@@_parse_css:n { \@@_parse_css:w #1 \q_mark {\q_nil} \q_stop +% \end{macrocode} +% +% Ensure the already seen specificities is in order without duplicates. +% +% \begin{macrocode} + \seq_remove_duplicates:N \l_@@_specificities_seq + \seq_sort:Nn \l_@@_specificities_seq { + \int_compare:nNnTF { ##1 } > { ##2 } + { \sort_return_swapped: } + { \sort_return_same: } + } } - -\tl_new:N \l_@@_parse_tmp_tl +% \end{macrocode} +% +% Grab the content up to the first opening brace. That content will be the +% comma-separated selector list, and the braced content is a block of +% properties. We can loop until there is no such block remaining. +% +% \begin{macrocode} +\tl_new:N \l_@@_parse_properties_tl \NewDocumentCommand \@@_parse_css:w { lmu{\q_stop} } { \quark_if_nil:nF {#2} { - \@@_parse_properties:Nn \l_@@_parse_tmp_tl {#2} + \@@_parse_properties:Nn \l_@@_parse_properties_tl {#2} \clist_map_inline:nn {#1} { - \@@_parse_css_addprops:nV {##1} \l_@@_parse_tmp_tl + \tl_if_empty:nF {##1} { \@@_parse_css_addprops:n {##1} } } \@@_parse_css:w #3 \q_stop } } +% \end{macrocode} +% +% Some pseudo-classes generate conditional code for the properties to be +% applied. Check if such code exists, and wrap the parsed property setters +% in a \cs{bool_if:nT}. +% +% \begin{macrocode} +\tl_new:N \l_@@_current_selector_tl +\tl_new:N \l_@@_current_selector_check_tl +\cs_new_protected:Nn \@@_parse_css_addprops:n { + \@@_parse_selector:n {#1} + \tl_if_empty:NF \l_@@_current_selector_tl { + \tl_set:Nx \l_tmpa_tl + { l_@@_property_group_\l_@@_current_selector_tl _tl } + \tl_if_exist:cF { \l_tmpa_tl } { \tl_clear:c { \l_tmpa_tl } } + \tl_if_empty:NTF \l_@@_current_selector_check_tl { + \tl_put_right:cV { \l_tmpa_tl } \l_@@_parse_properties_tl + }{ + \tl_put_right:cx { \l_tmpa_tl } { + \exp_not:N \bool_if:nT { + \exp_not:V \l_@@_current_selector_check_tl + }{ + \exp_not:V \l_@@_parse_properties_tl + } + } + } + } +} +% \end{macrocode} +% +% Here we parse a selector. These are naturally space-separated, but we first +% need to detect and normalize constructs like ":nth-child(argument)". We +% replace them by ":nth-child{argument}" where the braces will procect any +% space that can legitimately occur within "argument". +% +% \begin{macrocode} +\cs_new_protected:Nn \@@_parse_selector_sanitize:n { + \exp_args:Nx \@@_parse_selector_sanitize_aux:n + { \tl_to_str:n{#1} } +} +\cs_new_protected:Nn \@@_parse_selector_sanitize_aux:n { + \cs_set:Npn \@@_parse_selector_sanitize:w ##1:#1(##2)##3\q_stop + { + \quark_if_nil:nTF {##3} { + ##1 + }{ + ##1:#1{##2}\@@_parse_selector_sanitize:w ##3\q_stop + } + } + \tl_set:Nx \l_@@_current_selector_tl { + \exp_last_unbraced:NV + \@@_parse_selector_sanitize:w + \l_@@_current_selector_tl + :#1()\q_nil\q_stop + } +} +% \end{macrocode} +% +% Now that we can sanitize pseudo-classes, parsing the selector is safe. The +% constructs to protect are ":nth-child()" and ":where()" since other supported +% pseudo-classes have no argument. +% +% \begin{macrocode} +\seq_new:N \l_@@_current_selector_seq +\seq_new:N \l_@@_pseudoclasses_seq +\tl_new:N \l_@@_current_element_tl +\tl_new:N \l_@@_current_tableclass_tl +\int_new:N \l_@@_current_level_int +\int_new:N \l_@@_current_specificity_int +\seq_new:N \l_@@_specificities_seq +\cs_new_protected:Nn \@@_parse_selector:n { + \tl_set:Nx \l_@@_current_selector_tl { \tl_to_str:n {#1} } +% \end{macrocode} +% +% The sanitize code is more readable with Expl category colon, so replace it +% now, instead of defining the method with expand or lccode tricks. +% +% \begin{macrocode} + \exp_args:NNV \tl_replace_all:Nnn + \l_@@_current_selector_tl \c_colon_str {:} + \@@_parse_selector_sanitize:n {nth-child} + \@@_parse_selector_sanitize:n {where} + \seq_set_split:NnV \l_@@_current_selector_seq {~} \l_@@_current_selector_tl + \tl_clear:N \l_@@_current_selector_tl + \tl_clear:N \l_@@_current_selector_check_tl + \tl_clear:N \l_@@_current_tableclass_tl + \int_set:Nn \l_@@_current_level_int {-1} + \int_zero:N \l_@@_current_specificity_int + \seq_map_inline:Nn \l_@@_current_selector_seq { + \tl_clear:N \l_@@_current_element_tl + \@@_parse_simple_selector:n {##1} + } +% \end{macrocode} +% +% Remember the current selector as "<last element>~<specificity>". +% +% \begin{macrocode} + \tl_if_empty:NF \l_@@_current_element_tl { + \tl_if_empty:NF \l_@@_current_tableclass_tl { + \tl_set:Nx \l_@@_current_tableclass_tl { + l_@@_active_classes_\l_@@_current_tableclass_tl _bool + } + \bool_if_exist:cF { \l_@@_current_tableclass_tl } { + \bool_new:c { \l_@@_current_tableclass_tl } + } + \@@_add_check:x { + \exp_not:n { \bool_if_p:N } + { + \exp_not:c { \l_@@_current_tableclass_tl } + } + } + } + \tl_set:Nx \l_@@_current_selector_tl { + \exp_not:V \l_@@_current_element_tl + \exp_not:n {~} + \int_use:N \l_@@_current_specificity_int + } + \seq_put_right:Nx \l_@@_specificities_seq { + \int_use:N \l_@@_current_specificity_int + } + } +} +\tl_new:N \l_@@_maybe_element_tl -\seq_new:N \l_@@_parse_selector_seq -\tl_new:N \l_@@_parse_desc_tl - -\str_const:Nn \c_@@_parse_nthchild_str { :nth-child( } -\prop_new:N \c_@@_parse_replace_prop -\prop_put:Nnn \c_@@_parse_replace_prop { :first-child } { :nth-child(1) } - -\cs_new_protected:Nn \@@_parse_selector:Nn { - \str_set:Nx \l_tmpa_str {#2} +\cs_new_protected:Nn \@@_parse_simple_selector:n { +% \end{macrocode} +% +% First we replace "." with ":." so that class selectors can be handled with +% the same code as pseudo-classes. Then we split the current selector item on +% ":" to get the base element and the pseudo-classes. +% +% \begin{macrocode} + \tl_set:Nn \l_tmpa_tl { #1 } + \tl_replace_all:Nnn \l_tmpa_tl {.} {:.} + \seq_set_split:NnV \l_@@_pseudoclasses_seq {:} \l_tmpa_tl + \seq_pop_left:NN \l_@@_pseudoclasses_seq \l_@@_maybe_element_tl +% \end{macrocode} +% +% Known type selectors increase the specificity by one. +% +% \begin{macrocode} + \str_case:VnTF \l_@@_maybe_element_tl { + { table } { \int_set:Nn \l_tmpa_int { 1 } } + { tr } { \int_set:Nn \l_tmpa_int { 2 } } + { td } { \int_set:Nn \l_tmpa_int { 3 } } + { p } { \int_set:Nn \l_tmpa_int { 4 } } + }{ + \int_add:Nn \l_@@_current_specificity_int { 1 } + }{ % \end{macrocode} % -% Replace some aliases with their meaning: +% An empty type selector is accepted to account for ":where()" pseudo-classes. +% They will be handled in the loop below, but of course there should be a type +% selector eventually. Map that case to a big positive next level so that we can +% detect that and keep the current level as-is. +% +% Unknown type selectors are mapped to big negative levels +% so that the descendant check will refuse them, unless we are at the very +% first type selector in which case we map the deprecated environment +% selector to a class selector. % % \begin{macrocode} - \prop_map_inline:Nn \c_@@_parse_replace_prop { - \use:x { - \exp_not:n { \tl_replace_all:Nnn \l_tmpa_str } - { \tl_to_str:n { ##1 } } { \tl_to_str:n { ##2 } } + \tl_if_empty:NTF \l_@@_maybe_element_tl { + \int_set:Nn \l_tmpa_int { 10 } + }{ + \int_compare:nNnTF \l_@@_current_level_int = { -1 } { + \tl_set_eq:NN + \l_@@_current_tableclass_tl + \l_@@_maybe_element_tl + \tl_set:Nn \l_@@_maybe_element_tl { table } + \int_set:Nn \l_tmpa_int { 1 } + \int_add:Nn \l_@@_current_specificity_int { 11 } + }{ + \int_set:Nn \l_tmpa_int { -10 } + } } } % \end{macrocode} % -% Replace all spaces by \cs{q_stop} to defeat the space removal feature of -% \cs{seq_set_split:Nnn}. +% If no type selector has been found yet, record the one we may have found +% now. Else, refuse to have two in the same selector. % % \begin{macrocode} - \tl_replace_all:Nnn \l_tmpa_str {~} {\q_stop} - \exp_args:NNVV - \seq_set_split:Nnn \l_tmpa_seq \c_@@_parse_nthchild_str \l_tmpa_str - \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \tl_if_empty:NTF \l_@@_current_element_tl { + \tl_set_eq:NN \l_@@_current_element_tl \l_@@_maybe_element_tl + }{ + \tl_if_empty:NF \l_@@_maybe_element_tl { + \int_set:Nn \l_tmpa_int { -10 } + } + } % \end{macrocode} % -% And replace them back. +% If the detected element is a descendent of the previous one, the selector +% can match; count the element selector specificity and parse its +% pseudo-classes. If not, the whole selector cannot match and we discard it +% early. % % \begin{macrocode} - \tl_replace_all:Nnn \l_tmpa_tl {\q_stop} {~} - \seq_clear:N \l_@@_parse_selector_seq - \seq_put_right:NV \l_@@_parse_selector_seq \l_tmpa_tl - \seq_map_inline:Nn \l_tmpa_seq { - \tl_set:Nn \l_tmpa_tl { ##1 } + \int_compare:nNnTF \l_tmpa_int > \l_@@_current_level_int { + \int_compare:nNnT \l_tmpa_int < 10 { + \int_set_eq:NN \l_@@_current_level_int \l_tmpa_int + } + \seq_map_inline:Nn \l_@@_pseudoclasses_seq { + \@@_parse_pseudoclass:w ##1{}\q_stop + } + }{ + \tl_clear:N \l_@@_current_element_tl + } % \end{macrocode} % -% And replace them back again. +% At the end of the selector parsing, the current element should not be empty. % % \begin{macrocode} - \tl_replace_all:Nnn \l_tmpa_tl {\q_stop} {~} + \tl_if_empty:NT \l_@@_current_element_tl { + \seq_map_break: + } +} % \end{macrocode} % -% Now replace the first closing parenthesis by \cs{q_stop}\cs{prg_do_nothing:} -% to use \cs{q_stop} as a delimiter for \cs{seq_set_split:Nnn} and thus split -% at most once. Note that here the space trimming feature is desired for the -% left part, but not just at the right of the parenthesis, so -% \cs{prg_do_nothing:} will act as a guard and will be removed afterwards. +% The first argument is the complete pseudo-class up to the opening argument +% brace (if any), and the second argument is the braced content (if any). +% The third argument gobbles any trailing garbage. +% +% If the pseudo-class starts with a "." this is a class selector. Else, this +% is an unknown pseudo-class and we reject the complete selector. % % \begin{macrocode} - \tl_replace_once:Nnn \l_tmpa_tl { ) } { \q_stop\prg_do_nothing: } - \seq_set_split:NnV \l_tmpa_seq { \q_stop } \l_tmpa_tl - \seq_pop_right:NN \l_tmpa_seq \l_@@_parse_desc_tl - \tl_replace_once:Nnn \l_@@_parse_desc_tl { \prg_do_nothing: } {} - \seq_get_left:NNT \l_tmpa_seq \l_tmpa_tl { - \exp_args:NNV \@@_parse_nth:Nn \l_tmpa_tl \l_tmpa_tl - \tl_put_left:Nn \l_@@_parse_desc_tl { ) } - \tl_put_left:NV \l_@@_parse_desc_tl \l_tmpa_tl +\NewDocumentCommand \@@_parse_pseudoclass:w { lmu{\q_stop} } { + \str_case:nnF { #1 } { + {first-child} { \@@_parse_selector_nth:n {1} } + {nth-child} { \@@_parse_selector_nth:n {#2} } + {where} { \@@_parse_where:n {#2} } + }{ + \str_if_eq:eeTF { \str_head:n { #1 } } {.} { + \tl_set:Nx \l_@@_current_tableclass_tl { + \str_tail:n { #1 } + } + \int_add:Nn \l_@@_current_specificity_int { 10 } + }{ + \tl_clear:N \l_@@_current_element_tl + \seq_map_break:n { \seq_map_break: } } - \seq_put_right:NV \l_@@_parse_selector_seq \l_@@_parse_desc_tl - } - \tl_set:Nx #1 { - \exp_args:NNV \seq_use:Nn - \l_@@_parse_selector_seq \c_@@_parse_nthchild_str } } \str_const:Nn \c_@@_parse_n_str {n} -\seq_new:N \l_@@_used_nth_factors_seq -\cs_new_protected:Nn \@@_parse_nth:Nn { - \str_case:nnF {#2} { +\int_new:N \l_@@_nth_coeff_int +\int_new:N \l_@@_nth_offset_int +\cs_new_protected:Nn \@@_parse_selector_nth:n { +% \end{macrocode} +% +% Count the pseudo-class in the specificity. +% +% \begin{macrocode} + \int_add:Nn \l_@@_current_specificity_int { 10 } +% \end{macrocode} +% +% Now parse the nth-child argument: +% +% \begin{macrocode} + \str_case:nnF {#1} { {even} { \str_set:Nn \l_tmpa_str {2n} } {odd} { \str_set:Nn \l_tmpa_str {2n+1} } }{ - \str_set:Nn \l_tmpa_str {#2} + \str_set:Nn \l_tmpa_str {#1} } - \exp_args:NNVV - \seq_set_split:Nnn \l_tmpa_seq \c_@@_parse_n_str \l_tmpa_str + \exp_args:NNV + \seq_set_split:NnV \l_tmpa_seq \c_@@_parse_n_str \l_tmpa_str \seq_pop_right:NN \l_tmpa_seq \l_tmpa_tl - \int_set:Nn \l_tmpb_int { 0\l_tmpa_tl } + \tl_if_empty:NTF \l_tmpa_tl { + \int_zero:N \l_@@_nth_offset_int + }{ + \int_set:Nn \l_@@_nth_offset_int { \l_tmpa_tl } + } \seq_get_left:NNTF \l_tmpa_seq \l_tmpa_tl { - \int_set:Nn \l_tmpa_int { 0\l_tmpa_tl } + \tl_if_empty:NTF \l_tmpa_tl { + \int_set:Nn \l_@@_nth_coeff_int {1} + }{ + \exp_args:NV \tl_if_eq:nnTF \l_tmpa_tl {-} { + \int_set:Nn \l_@@_nth_coeff_int {-1} + }{ + \int_set:Nn \l_@@_nth_coeff_int { \l_tmpa_tl } + } + } }{ - \int_zero:N \l_tmpa_int + \int_zero:N \l_@@_nth_coeff_int + } +% \end{macrocode} +% +% At last, generate the condition code. +% +% \begin{macrocode} + \str_case:Vn \l_@@_current_element_tl { + {tr} { \@@_generate_check_nth:n {\g_@@_row_int} } + {td} { \@@_generate_check_nth:n {\g_@@_col_int} } } - \int_compare:nNnTF \l_tmpa_int = { 0 } { - \tl_set:Nx #1 { \int_use:N \l_tmpb_int } +} + +\cs_new_protected_nopar:Nn \@@_generate_check_nth:n { + \int_compare:nNnTF \l_@@_nth_coeff_int = { 0 } { + \@@_add_check:x { + \exp_not:n { \int_compare_p:nNn #1 = } + { + \exp_not:V \l_@@_nth_offset_int + } + } }{ - \int_set:Nn \l_tmpb_int { \int_mod:nn {\l_tmpb_int} {\l_tmpa_int} } - \tl_set:Nx #1 { - \int_use:N \l_tmpa_int \exp_not:V \c_@@_parse_n_str - + \int_use:N \l_tmpb_int } - \seq_put_right:Nx - \l_@@_used_nth_factors_seq { \int_use:N \l_tmpa_int } + \tl_set:Nx \l_tmpb_tl { + { + \exp_not:n { #1 - } + \exp_not:V \l_@@_nth_offset_int + }{ + \exp_not:V \l_@@_nth_coeff_int + } + } + \@@_add_check:x { + \exp_not:N \bool_lazy_and_p:nn { + \exp_not:n { \int_compare_p:nNn 0 = } + { + \exp_not:N \int_mod:nn + \exp_not:V \l_tmpb_tl + } + }{ + \exp_not:n { \int_compare_p:nNn 0 < } + { + \exp_not:N \int_div_truncate:nn + \exp_not:V \l_tmpb_tl + \exp_not:n { + 1 } + } + } + } } } -\cs_new_protected:Npn \@@_parse_css_addprops:nV #1 #2 { - \@@_parse_selector:Nn \l_tmpa_tl {#1} - \tl_set:Nx \l_tmpa_tl { l_@@_property_group_\l_tmpa_tl _tl } - \tl_if_exist:cF { \l_tmpa_tl } { \tl_clear:c { \l_tmpa_tl } } - \tl_put_right:cV { \l_tmpa_tl } #2 +\cs_new_protected:Nn \@@_parse_where:n { + \use:x { + \exp_not:n { + \@@_parse_simple_selector:n { #1 } + \int_set:Nn \l_@@_current_specificity_int + } + { + \int_use:N \l_@@_current_specificity_int + } + } } -\cs_set_protected:Nn \@@_recall_properties:n { - \tl_if_exist:cT { l_@@_property_group_#1_tl } { - \tl_use:c { l_@@_property_group_#1_tl } +\cs_new_protected:Nn \@@_add_check:n { + \tl_if_empty:NTF \l_@@_current_selector_check_tl { + \tl_set:Nn \l_@@_current_selector_check_tl { #1 } + }{ + \tl_set:Nx \l_@@_current_selector_check_tl { + \exp_not:N \bool_lazy_and_p:nn { + \exp_not:V \l_@@_current_selector_check_tl + }{ + \exp_not:n { #1 } + } + } + } +} +\cs_generate_variant:Nn \@@_add_check:n {x} + +\seq_new:N \l_@@_classes_seq +\NewDocumentCommand \cellpropsclass { sm } { + \seq_set_split:Nnn \l_@@_classes_seq {~} { #2 } + \IfBooleanF {#1} { + \seq_put_right:Nn \l_@@_classes_seq { \@currenvir } } - \clist_map_inline:nn { \@currenvir } { - \tl_if_exist:cT { l_@@_property_group_##1~#1_tl } { - \tl_use:c { l_@@_property_group_##1~#1_tl } +} +\cellpropsclass{} + +\cs_set_protected:Nn \@@_recall_properties:n { + \seq_map_inline:Nn \l_@@_specificities_seq { + \tl_if_exist:cT { l_@@_property_group_#1~##1_tl } { + \tl_use:c { l_@@_property_group_#1~##1_tl } } } } @@ -795,22 +1222,23 @@ \ExplSyntaxOff \cellprops{ - td { + :where(td) { padding: 0pt \csname l_@@_colsep_dim\endcsname; min-height: \csname l_@@_strut_ht_dim\endcsname; min-depth: \csname l_@@_strut_dp_dim\endcsname; min-width: 0pt; text-align: left; + vertical-align: baseline; math-mode: auto; color: inherit; background-color: transparent; border: thin none inherit; } - tr { + :where(tr) { color: inherit; background-color: transparent; } - table { + :where(table) { padding: 0pt; % No change at load time color: inherit; background-color: transparent; @@ -837,7 +1265,8 @@ \tl_new:N \l_@@_color_tl \tl_new:N \l_@@_bgcolor_tl -% To count rows and columns +\seq_new:N \l_@@_classes_at_start_seq + \cs_new_protected:Nn \@@_array_init: { \tl_set:Nx \l_@@_restore_tl { \bool_if:NTF \g_@@_inrow_bool { @@ -856,6 +1285,23 @@ \exp_not:n { \tl_gset:Nn \g_@@_borders_tl } { \exp_not:V \g_@@_borders_tl } } +% \end{macrocode} +% +% Unset all previous active classes, and set current ones as active. +% +% \begin{macrocode} + \seq_map_inline:Nn \l_@@_classes_at_start_seq { + \bool_set_false:c { l_@@_active_classes_##1_bool } + } + \seq_set_eq:NN \l_@@_classes_at_start_seq \l_@@_classes_seq + \seq_map_inline:Nn \l_@@_classes_at_start_seq { + \bool_set_true:c { l_@@_active_classes_##1_bool } + } +% \end{macrocode} +% +% To count rows and columns. +% +% \begin{macrocode} \int_gzero:N \g_@@_row_int \bool_gset_false:N \g_@@_inrow_bool \tl_gclear:N \g_@@_borders_tl @@ -887,7 +1333,6 @@ \dim_set_eq:NN \l_@@_tablepadding_bottom_dim \g_tmpb_dim \tl_set_eq:NN \l_@@_color_tl \g_tmpa_tl \tl_set_eq:NN \l_@@_bgcolor_tl \g_tmpb_tl - \@@_recall_properties:n {tr} \dim_set:Nn \l_@@_strut_ht_dim { \box_ht:N \@arstrutbox } \dim_set:Nn \l_@@_strut_dp_dim { \box_dp:N \@arstrutbox } \box_clear:N \@arstrutbox @@ -897,13 +1342,46 @@ \hlx{s[\l_@@_tablepadding_top_dim]} } +\cs_new_protected_nopar:Nn \@@_maybe_startrow: { + \bool_if:NF \g_@@_inrow_bool { + \bool_gset_true:N \g_@@_inrow_bool + \int_gincr:N \g_@@_row_int + \int_gset_eq:NN \g_@@_col_int \c_one_int + \dim_gzero:N \g_@@_ht_dim + \dim_gzero:N \g_@@_dp_dim + } +} + +\cs_new_protected_nopar:Nn \@@_maybe_endrow: { + \bool_if:NT \g_@@_inrow_bool { + \@@_every_cell_end: + \bool_gset_false:N \g_@@_inrow_bool + } +} + +\cs_new_protected_nopar:Nn \@@_every_cell_end: { + \int_gincr:N \g_@@_col_int +} + \cs_set_protected_nopar:Nn \@@_readpreamble:n { \cs_set_eq:NN \tab@readpreamble \@@_orig_tab@readpreamble:n - \tl_put_left:Nn \tab@multicol {\@@_startrow:} - \tl_put_left:Nn \tab@tabtext {\int_gincr:N \g_@@_col_int} +% \end{macrocode} +% +% \cs{tab@multicol} is inserted at the beginning of a each row, and by +% \cs{multicolumn} after its \cs{omit}. We use it to ensure that the row is +% initialized correctly however it starts (normally or with a +% \cs{multicolumn}). +% +% \cs{tab@tabtext} is inserted at the end of every cell but the last one, so +% we should ensure that its effect is applied at the end of the row; +% \cs{@@_maybe_endrow} will take care of that. +% +% \begin{macrocode} + \tl_put_left:Nn \tab@multicol {\@@_maybe_startrow:} + \tl_put_left:Nn \tab@tabtext {\@@_every_cell_end:} \tab@readpreamble{#1} \exp_args:Nx \tab@preamble - { \exp_not:N\@@_startrow: \the\tab@preamble \exp_not:N\@@_endrow: } + { \the\tab@preamble \exp_not:N\@@_maybe_endrow: } } % \end{macrocode} % @@ -976,24 +1454,8 @@ \@@_end_array:n { \@@_orig_endLT: } } -\cs_new_protected_nopar:Nn \@@_startrow: { - \bool_if:NF \g_@@_inrow_bool { - \bool_gset_true:N \g_@@_inrow_bool - \int_gincr:N \g_@@_row_int - \int_gset_eq:NN \g_@@_col_int \c_one_int - \dim_gzero:N \g_@@_ht_dim - \dim_gzero:N \g_@@_dp_dim - } -} - -\cs_new_protected_nopar:Nn \@@_endrow: { - \bool_if:NT \g_@@_inrow_bool { - \bool_gset_false:N \g_@@_inrow_bool - } -} - \cs_new_protected_nopar:Nn \@@_cr:n { - \@@_endrow: + \@@_maybe_endrow: \tl_if_empty:NF \g_@@_borders_tl { \cr \noalign{\nobreak} @@ -1016,10 +1478,12 @@ \cs_set_eq:NN \@@_orig_multicolumn:w \multicolumn \cs_set:Npn \multicolumn#1#2#3 { - \@@_orig_multicolumn:w {#1}{#2}{ - #3 - \int_gadd:Nn \g_@@_col_int {#1} + \@@_orig_multicolumn:w {#1}{#2}{#3} + \int_gadd:Nn \g_@@_col_int {#1} + \tl_gput_right:Nx \g_@@_borders_tl { + \prg_replicate:nn {#1 - 1} {\span\omit} } + \ignorespaces } } @@ -1110,20 +1574,6 @@ \colpop -% \end{macrocode} -% -% Handle various \verb|:nth-child()| forms. -% -% \begin{macrocode} -\cs_new_protected_nopar:Nn \@@_seq_nthchild:Nn { - \seq_clear:N #1 - \seq_map_inline:Nn \l_@@_used_nth_factors_seq { - \seq_put_right:Nx #1 { - ##1 n + \int_eval:n{\int_mod:nn{#2}{##1}} - } - } - \seq_put_right:Nx #1 { \int_eval:n{#2} } -} \cs_new_protected_nopar:Nn \@@_begincell:n { \@@_begin_raw_cell:n { @@ -1162,25 +1612,7 @@ \global\@minipagefalse \everypar{} } - \@@_seq_nthchild:Nn \l_tmpa_seq { \g_@@_row_int } - \@@_seq_nthchild:Nn \l_tmpb_seq { \g_@@_col_int } - \@@_recall_properties:n {td~p} - \seq_map_inline:Nn \l_tmpa_seq { - \@@_recall_properties:n {tr:nth-child(##1)~p} - } - \seq_map_inline:Nn \l_tmpb_seq { - \@@_recall_properties:n {td:nth-child(##1)~p} - } - \@@_recall_properties:n {tr~td~p} - \seq_map_inline:Nn \l_tmpa_seq { - \@@_recall_properties:n {tr:nth-child(##1)~td~p} - } - \seq_map_inline:Nn \l_tmpa_seq { - \seq_map_inline:Nn \l_tmpb_seq { - \@@_recall_properties:n {tr:nth-child(##1)~ - td:nth-child(####1)~p} - } - } + \@@_recall_properties:n {p} } } \cs_new_protected_nopar:Nn \@@_end_par_cell:n { @@ -1195,26 +1627,9 @@ \cs_new_protected_nopar:Nn \@@_begin_raw_cell:n { \group_begin: - \@@_seq_nthchild:Nn \l_tmpa_seq { \g_@@_row_int } - \@@_seq_nthchild:Nn \l_tmpb_seq { \g_@@_col_int } - \seq_map_inline:Nn \l_tmpa_seq { - \@@_recall_properties:n {tr:nth-child(##1)} - } + \@@_recall_properties:n {tr} \@@_update_colors: \@@_recall_properties:n {td} - \@@_recall_properties:n {tr~td} - \seq_map_inline:Nn \l_tmpb_seq { - \@@_recall_properties:n {td:nth-child(##1)} - } - \seq_map_inline:Nn \l_tmpa_seq { - \@@_recall_properties:n {tr:nth-child(##1)~td} - } - \seq_map_inline:Nn \l_tmpa_seq { - \seq_map_inline:Nn \l_tmpb_seq { - \@@_recall_properties:n {tr:nth-child(##1)~ - td:nth-child(####1)} - } - } \@@_update_colors: % Additional init code #1 @@ -1302,12 +1717,50 @@ \tl_if_empty:NF \g_@@_borders_tl { \exp_not:n {&} } \exp_not:n { \omit \kern \c_zero_dim } } - % Handle padding-top, min-height and border-top - \@@_get_border_info:n {top} + % Handle min-height, min-depth and vertical-align + % wrong values are treated as |baseline|. \box_set_ht:Nn \l_@@_cell_box { \dim_max:nn {\box_ht:N \l_@@_cell_box} {\@@_get_property:n {min-height}} + } + \box_set_dp:Nn \l_@@_cell_box { + \dim_max:nn + {\box_dp:N \l_@@_cell_box} + {\@@_get_property:n {min-depth}} + } + \str_case_e:nn {\@@_get_property:n {vertical-align}} { + { top } { + \hbox_set:Nn \l_@@_cell_box { + \vbox_top:n { + \kern 0pt\relax + \box_use_drop:N \l_@@_cell_box + } + } + } + { bottom } { + \hbox_set:Nn \l_@@_cell_box { + \vbox:n { + \box_use_drop:N \l_@@_cell_box + \kern 0pt\relax + } + } + } + { middle } { + \hbox_set:Nn \l_@@_cell_box { + \dim_set:Nn \l_tmpa_dim { + (\box_dp:N \l_@@_cell_box + - \box_ht:N \l_@@_cell_box + + 1ex) / 2 + } + \raisebox{\l_tmpa_dim}{\box_use_drop:N \l_@@_cell_box} + } + } + } + % Handle padding-top and border-top + \@@_get_border_info:n {top} + \box_set_ht:Nn \l_@@_cell_box { + \box_ht:N \l_@@_cell_box + (\@@_get_property:n {padding-top}) + \l_@@_border_width_dim } @@ -1323,12 +1776,10 @@ { \exp_not:V \l_@@_border_color_tl } } } - % Handle padding-bottom, min-depth and border-bottom + % Handle padding-bottom and border-bottom \@@_get_border_info:n {bottom} \box_set_dp:Nn \l_@@_cell_box { - \dim_max:nn - {\box_dp:N \l_@@_cell_box} - {\@@_get_property:n {min-depth}} + \box_dp:N \l_@@_cell_box + (\@@_get_property:n {padding-bottom}) + \l_@@_border_width_dim } diff --git a/Master/texmf-dist/source/latex/cellprops/cellprops.ins b/Master/texmf-dist/source/latex/cellprops/cellprops.ins index 195394a7e74..1b50038e33a 100644 --- a/Master/texmf-dist/source/latex/cellprops/cellprops.ins +++ b/Master/texmf-dist/source/latex/cellprops/cellprops.ins @@ -1,6 +1,6 @@ \iffalse meta-comment -File cellprops.ins (C) Copyright 2016-2019 RIVAUD Julien +File cellprops.ins (C) Copyright 2016-2021 RIVAUD Julien It may be distributed and/or modified under the conditions of the General Public License (GPL), either version 3 of this |