%% This is part of OpTeX project, see http://petr.olsak.net/optex \_codedecl \table {Basic macros for OpTeX <2020-05-20>} % preloaded in format \_doc ----------------------------- The result of the \`\table``{}{}` macro is inserted into `\_tablebox`. You can change default value if you want by `\let\_tablebox=\vtop` or `\let\_tablebox=\relax`. \_cod ----------------------------- \_let\_tablebox=\_vbox \_doc ----------------------------- We save the `to` or `pxto` to `#1` and \`\_tableW` sets the `to` to the \`\_tablew` macro. If `pxto` is used then `\_tablew` is empty and `\_tmpdim` includes given . The `\_ifpxto` returns true in this case. The `\table` continues by reading `{}` in the \^`\_tableA` macro. Catcodes (for example the `|` character) have to be normal when reading `\table` parameters. This is the reason why we use `\catcodetable` here. \_cod ----------------------------- \_newifi \_ifpxto \_def\_table#1#{\_tablebox\_bgroup \_tableW#1\_empty\_end \_bgroup \_catcodetable\_optexcatcodes \_tableA} \_def\_tableW#1#2\_end{\_pxtofalse \_ifx#1\_empty \_def\_tablew{}\_else \_ifx#1p \_def\_tablew{}\_tableWx#2\_end \_else \_def\_tablew{#1#2}\_fi\_fi} \_def\_tableWx xto#1\_end{\_tmpdim=#1\_relax \_pxtotrue} \_public \table ; \_doc ----------------------------- The \^`\tablinespace` is implemented by enlarging given \^`\tabstrut` by desired dimension (height and depth too) and by setting `\_lineskip=-2\_tablinespace`. Normal table rows (where no `\hrule` is between them) have normal baseline distance.\nl The \`\_tableA``{}` macro scans the `` by `\_scantabdata#1\_relax` and continues by reading `{}` by the \^`\_tableB` macro. \_cod ----------------------------- \_def\_tableA#1{\_egroup \_the\_thistable \_global\_thistable={}% \_ea\_ifx\_ea^\_the\_tabstrut^\_setbox\_tstrutbox=\_null \_else \_setbox\_tstrutbox=\_hbox{\_the\_tabstrut}% \_setbox\_tstrutbox=\_hbox{\_vrule width0pt height\_dimexpr\_ht\_tstrutbox+\_tablinespace depth\_dimexpr\_dp\_tstrutbox+\_tablinespace}% \_offinterlineskip \_lineskip=-2\_tablinespace \_fi \_colnum=0 \_let\_addtabitem=\_addtabitemx \_def\_tmpa{}\_tabdata={\_colnum1\_relax}\_scantabdata#1\_relax \_the\_everytable \_tableB } \_doc ----------------------------- The \`\_tableB``{}` saves `` to `\_tmpb` and does four \^`\replstring`s to prefix each macro \^`\crli` (etc.) by `\_crcr`. The reason is: we want to use macros which scan its parameter to the delimeiter written in right part of table item declaration. See \^`\fS` for example. The `\crcr` cannot be hidden in other macro in such case. The `\tabskip` value is saved for places between columns into the \`\_tabskipmid` macro. Then it runs \begtt \catcode`\<=13 \tabskip=\tabskipl \halign{\tabskip=\tabskipr \cr \crcr} \endtt This sets the desired boundary values of `\tabskip`. The \"between-columns" values are set as `\tabskip=`\^`\_tabskipmid` in the `` immediately after each column declarator. If `pxto` keyword was used, then we set the virtual unit \^`\tsize` to `\hsize` first. Then the first attempt of the table is created in box 0. Then the \^`\tsize` is re-calculated using `\wd0` and the real table is printed by `\halign` in the second pass. If no `pxto` keyword was used, then we print the table using `\halign` directly. The \^`\_tablew` macro is nonempty if the `to` keyword was used. Because the color selector with `\aftergroup` can be used inside the table item, we must to create second real group for each table item. This is reason why we start `` by `\bgroup` and we end it by `\egroup` in the \`\_tableC` macro. Each `&` character is stored as `\egroup&\bgroup` in ``. The `\halign\_tablew\_tableC` really does: \begtt \catcode`\<=13 \halign\_tablew{\bgroup\egroup\tabskip=\tabskipr \cr\crcr} \endtt \relax \_cod ----------------------------- \_def\_tableB#1{\_def\_tmpb{#1}% \_replstring\_tmpb{\crli}{\_crcr\crli}\_replstring\_tmpb{\crll}{\_crcr\crll}% \_replstring\_tmpb{\crlli}{\_crcr\crlli}\_replstring\_tmpb{\crlp}{\_crcr\crlp}% \_edef\_tabskipmid{\_the\_tabskip}\_tabskip=\_tabskipl \_ifpxto \_tsize=\_hsize \_setbox0 = \_vbox{\_halign \_tableC}% \_tsize=\_dimexpr\_hsize-(\_wd0-\_tmpdim)\_relax \_setbox0=\_null \_halign \_tableC \_else \_halign\_tablew \_tableC \_fi \_egroup } \_def\_tableC{\_ea{\_ea\_bgroup\_the\_tabdata\_egroup\_tabskip=\_tabskipr\_cr\_tmpb\_crcr}} \_newbox\_tstrutbox % strut used in table rows \_newtoks\_tabdata % the \halign declaration line \_doc ----------------------------- The \`\_scantabdata` macro converts `\table`'s `` to `\halign` ``. The result is stored into \`\_tabdata` tokens list. For example, the following result is generated when `=|cr||cl|`. \begtt tabdata: \_vrule\_the\_tabiteml\_hfil#\_unsskip\_hfil\_the\_tabitemr\_tabstrutA &\_the\_tabiteml\_hfil#\_unsskip\_the\_tabitemr \_vrule\_kern\_vvkern\_vrule\_tabstrutA &\_the\_tabiteml\_hfil#\_unsskip\_hfil\_the\_tabitemr\_tabstrutA &\_the\_tabiteml#\_unsskip\_hfil\_the\_tabitemr\_vrule\_tabstrutA ddlinedata: &\_dditem &\_dditem\_vvitem &\_dditem &\_dditem \endtt The second result in the \`\_ddlinedata` macro is a template of one row of the table used by \^`\crli` macro. \_cod ----------------------------- \_def\_scantabdata#1{\_let\_next=\_scantabdata \_ifx\_relax#1\_let\_next=\_relax \_else\_ifx|#1\_addtabvrule \_else\_ifx(#1\_def\_next{\_scantabdataE}% \_else\_ifx:#1\_def\_next{\_scantabdataF}% \_else\_isinlist{123456789}#1\_iftrue \_def\_next{\_scantabdataC#1}% \_else \_ea\_ifx\_csname _tabdeclare#1\_endcsname \_relax \_ea\_ifx\_csname _paramtabdeclare#1\_endcsname \_relax \_opwarning{tab-declarator "#1" unknown, ignored}% \_else \_def\_next{\_ea\_scantabdataB\_csname _paramtabdeclare#1\_endcsname}\_fi \_else \_def\_next{\_ea\_scantabdataA\_csname _tabdeclare#1\_endcsname}% \_fi\_fi\_fi\_fi\_fi\_fi \_next } \_def\_scantabdataA#1{\_addtabitem \_ea\_addtabdata\_ea{#1\_tabstrutA \_tabskip\_tabskipmid}\_scantabdata} \_def\_scantabdataB#1#2{\_addtabitem \_ea\_addtabdata\_ea{#1{#2}\_tabstrutA \_tabskip\_tabskipmid}\_scantabdata} \_def\_scantabdataC {\_def\_tmpb{}\_afterassignment\_scantabdataD \_tmpnum=} \_def\_scantabdataD#1{\_loop \_ifnum\_tmpnum>0 \_advance\_tmpnum by-1 \_addto\_tmpb{#1}\_repeat \_ea\_scantabdata\_tmpb} \_def\_scantabdataE#1){\_addtabdata{#1}\_scantabdata} \_def\_scantabdataF {\_addtabitem\_def\_addtabitem{\_let\_addtabitem=\_addtabitemx}\_scantabdata} \_doc ----------------------------- The \`\_addtabitemx` adds the boundary code (used between columns) to the . This code is `\egroup &\bgroup \colnum=\relax`. You can get the current number of column from the \`\colnum` register, but you cannot write `\the\colnum` as the first object in a item because `\halign` first expands the front of the item and the left part of the declaration is processed after this. Use `\relax\the\colnum` instead. Or you can write: \begtt \def\showcolnum{\ea\def\ea\totcolnum\ea{\the\colnum}\the\colnum/\totcolnum} \table{ccc}{\showcolnum & \showcolnum & \showcolnum} \endtt This example prints 1/3 \ 2/3 \ 3/3, because the value of the `\colnum` is equal to the total number of columns before left part of the column declaration is processed. \_cod ----------------------------- \_newcount\_colnum % number of current column in the table \_public \colnum ; \_def\_addtabitemx{\_ifnum\_colnum>0 \_addtabdata{\_egroup &\_bgroup}\_addto\_ddlinedata{&\_dditem}\_fi \_advance\_colnum by1 \_let\_tmpa=\_relax \_ifnum\_colnum>1 \_ea\_addtabdata\_ea{\_ea\_colnum\_the\_colnum\_relax}\_fi} \_def\_addtabdata#1{\_tabdata\_ea{\_the\_tabdata#1}} \_doc ----------------------------- This code converts `||` or `|` from `\table` to the . \_cod ----------------------------- \_def\_addtabvrule{% \_ifx\_tmpa\_vrule \_addtabdata{\_kern\_vvkern}% \_ifnum\_colnum=0 \_addto\_vvleft{\_vvitem}\_else\_addto\_ddlinedata{\_vvitem}\_fi \_else \_ifnum\_colnum=0 \_addto\_vvleft{\_vvitemA}\_else\_addto\_ddlinedata{\_vvitemA}\_fi\_fi \_let\_tmpa=\_vrule \_addtabdata{\_vrule}% } \_def\_tabstrutA{\_copy\_tstrutbox} \_def\_vvleft{} \_def\_ddlinedata{} \_doc ----------------------------- The default \"declaration letters" `c`, `l`, `r` and `p` are declared by setting \`\tabdeclarec`, \`\tabdeclarel`, \`\tabdeclarer` and \`\paramtabdeclarep` macros. In general, define `\def\_tabdeclare{...}` for a non-parametric letter and `\def\_paramtabdeclare{...}` for a letter with a parameter. The double hash `##` must be in the definition, it is replaced by a real table item data. You can declare more such \"declaration letters" if you want. \_cod ----------------------------- \_def\_tabdeclarec{\_the\_tabiteml\_hfil##\_unsskip\_hfil\_the\_tabitemr} \_def\_tabdeclarel{\_the\_tabiteml\_relax##\_unsskip\_hfil\_the\_tabitemr} \_def\_tabdeclarer{\_the\_tabiteml\_hfil##\_unsskip\_the\_tabitemr} \_def\_paramtabdeclarep#1{\_the\_tabiteml \_vtop{\_hsize=#1\_relax \_baselineskip=\_normalbaselineskip \_lineskiplimit=0pt \_noindent##\_unsskip \_ifvmode\_vskip\_dp\_tstrutbox \_else\_lower\_dp\_tstrutbox\_hbox{}\_fi}\_the\_tabitemr} \_doc ----------------------------- User puts optional spaces around the table item typically, i.e.\ he/she writes `& text &` instead `&text&`. The left space is ignored by internal \TeX/ algorithm but the right space must be removed by macros. This is a reason why we recommend to use \`\_unsskip` after each `##` in your definition of \"declaration letters". This macro isn't only the primitive `\unskip` because we allow usage of plain \TeX/ `\hideskip` macro: `&\hideskip text\hideskip&`. \_cod ----------------------------- \_def\_unsskip{\_ifmmode\_else\_ifdim\_lastskip>0pt \_unskip\_fi\_fi} \_doc ----------------------------- The \`\fL`, \`\fR`, \`\fC` and \`\fX` macros only does a special parameters settings for paragraph building algorithm. The \`\fS` prints the paragraph into box 0 first, measures the number of lines by the `\prevgraf` primitive and use (or don't use) `\hfil` (for centering) before the first line. \_cod ----------------------------- \_let\_fL=\_raggedright \_def\_fR{\_leftskip=0pt plus 1fill \_relax} \_def\_fC{\_leftskip=0pt plus1fill \_rightskip=0pt plus 1fill \_relax} \_def\_fX{\_leftskip=0pt plus1fil \_rightskip=0pt plus-1fil \_parfillskip=0pt plus2fil \_relax} \_long\_def\_fS #1\_unsskip{\_noindent \_setbox0 =\_vbox{\_noindent #1\_endgraf \_ea}% \_ifnum\_prevgraf=1 \_hfil \_fi #1\_unsskip } \_public \fL \fR \fC \fX \fS ; \_doc ----------------------------- The family of `\_cr*` macros \`\crl`, \`\crll`, \`\crli`, \`\crlli`, \`\crlp` and \`\tskip` `` is implemented here. The \`\_zerotabrule` is used in order to suppress the negative `\lineskip` declared by \^`\tablinespace`. \_cod ----------------------------- \_def\_crl{\_crcr\_noalign{\_hrule}} \_def\_crll{\_crcr\_noalign{\_hrule\_kern\_hhkern\_hrule}} \_def\_zerotabrule {\_noalign{\_hrule height0pt width0pt depth0pt}} \_def\_crli{\_crcr \_zerotabrule \_omit \_gdef\_dditem{\_omit\_tablinefil}\_gdef\_vvitem{\_kern\_vvkern\_vrule}\_gdef\_vvitemA{\_vrule}% \_vvleft\_tablinefil\_ddlinedata\_crcr \_zerotabrule} \_def\_crlli{\_crli\_noalign{\_kern\_hhkern}\_crli} \_def\_tablinefil{\_leaders\_hrule\_hfil} \_def\_crlp#1{\_crcr \_zerotabrule \_noalign{\_kern-\_drulewidth}% \_omit \_xdef\_crlplist{#1}\_xdef\_crlplist{,\_expandafter}\_expandafter\_crlpA\_crlplist,\_end,% \_global\_tmpnum=0 \_gdef\_dditem{\_omit\_crlpD}% \_gdef\_vvitem{\_kern\_vvkern\_kern\_drulewidth}\_gdef\_vvitemA{\_kern\_drulewidth}% \_vvleft\_crlpD\_ddlinedata \_global\_tmpnum=0 \_crcr \_zerotabrule} \_def\_crlpA#1,{\_ifx\_end#1\_else \_crlpB#1-\_end,\_expandafter\_crlpA\_fi} \_def\_crlpB#1#2-#3,{\_ifx\_end#3\_xdef\_crlplist{\_crlplist#1#2,}\_else\_crlpC#1#2-#3,\_fi} \_def\_crlpC#1-#2-#3,{\_tmpnum=#1\_relax \_loop \_xdef\_crlplist{\_crlplist\_the\_tmpnum,}\_ifnum\_tmpnum<#2\_advance\_tmpnum by1 \_repeat} \_def\_crlpD{\_global\_advance\_tmpnum by1 \_edef\_tmpa{\_noexpand\_isinlist\_noexpand\_crlplist{,\_the\_tmpnum,}}% \_tmpa\_iftrue \_kern-\_drulewidth \_tablinefil \_kern-\_drulewidth\_else\_hfil \_fi} \_def\_tskip{\_afterassignment\_tskipA \_tmpdim} \_def\_tskipA{\_gdef\_dditem{}\_gdef\_vvitem{}\_gdef\_vvitemA{}\_gdef\_tabstrutA{}% \_vbox to\_tmpdim{}\_ddlinedata \_crcr \_zerotabrule \_noalign{\_gdef\_tabstrutA{\_copy\_tstrutbox}}} \_public \crl \crll \crli \crlli \crlp \tskip ; \_doc ----------------------------- The \`\mspan``[]{}` macro generates similar `\omit\span\omit\span` sequence as plain \TeX/ macro `\multispan`. Moreover, it uses \^`\_scantabdata` to convert `` from `\table` syntax to `\halign` syntax. \_cod ----------------------------- \_def\_mspan{\_omit \_tabdata={\_tabstrutA}\_let\_tmpa=\_relax \_afterassignment\_mspanA \_mscount=} \_def\_mspanA[#1]#2{\_loop \_ifnum\_mscount>1 \_cs{_span}\_omit \_advance\_mscount-1 \_repeat \_count1=\_colnum \_colnum=0 \_def\_tmpa{}\_tabdata={}\_scantabdata#1\_relax \_colnum=\_count1 \_setbox0=\_vbox{\_halign\_ea{\_ea\_bgroup\_the\_tabdata\_egroup\_cr#2\_cr}% \_global\_setbox8=\_lastbox}% \_setbox0=\_hbox{\_unhbox8 \_unskip \_global\_setbox8=\_lastbox}% \_unhbox8 \_ignorespaces} \_public \mspan ; \_doc ----------------------------- The \`\vspan``{}{}` implementaiton is here. We need to lower the box by \begtt \catcode`\<=13 (-1)*(\ht+\dp of \tabstrut) / 2. \endtt The `#1` parameter must be one-digit number. If you want to set more digits then use braces. \_cod ----------------------------- \_def\_vspan#1#2{\_vtop to 0pt{\_hbox{\_lower \_dimexpr #1\_dimexpr(\_ht\_tstrutbox+\_dp\_tstrutbox)/2\_relax -\_dimexpr(\_ht\_tstrutbox+\_dp\_tstrutbox)/2\_relax \_hbox{#2}}\_vss}} \_public \vspan ; \_doc ----------------------------- The parameters of primitive `\vrule` and `\hrule` keeps the rule \"last wins". If we re-define `\hrule` to `\_orihrule height1pt` then each usage of redefined `\hrule` uses `1pt` height if this parameter isn't overwritten by another following `height` parameter. This principle is used for settings another default rule thickness than 0.4\,pt by the macro \`\rulewidth`. \_cod ----------------------------- \_newdimen\_drulewidth \_drulewidth=0.4pt \_let\_orihrule=\_hrule \_let\_orivrule=\_vrule \_def\_rulewidth{\_afterassignment\_rulewidthA \_drulewidth} \_def\_rulewidthA{\_edef\_hrule{\_orihrule height\_drulewidth}% \_edef\_vrule{\_orivrule width\_drulewidth}% \_let\_rulewidth=\_drulewidth \_public \vrule \hrule \rulewidth;} \_public \rulewidth ; \_doc ----------------------------- The \`\frame``{}` uses \"\code{\\vbox} in \code{\\vtop}" trick in order to keep the baseline of the internal text at the same level as outer baseline. User can write `\frame{abcxyz}` in normal paragraph line, for example and gets the expected result: \frame{abcxyz}. The internal margins are set by `\vvkern` and `\hhkern` parameters. \_cod ----------------------------- \_long\_def\_frame#1{% \_hbox{\_vrule\_vtop{\_vbox{\_hrule\_kern\_vvkern \_hbox{\_kern\_hhkern\_relax#1\_kern\_hhkern}% }\_kern\_vvkern\_hrule}\_vrule}} \_public \frame ; \_endcode % ------------------------------------- \secc[table.bound] The boundary declarator \code{:} The `` part of `\table{}{}` includes column declarators (letters) and other material: the `|` or `()`. The boundaries of columns are just before each column declarator (with exception of the first one) if the boundary declarator `:` is not used. For example, the declaration `{|c||c(xx)(yy)c}` should be written more exactly using the boundary declarator `:` by `{|c||:c(xx)(yy):c}`. But you can set these boundaries to another places using the boundary declarator~`:` explicitly, for example `{|c:||c(xx):(yy)c}`. The boundary declarator `:` can be used only once between each two column declarators. Each table item have its own group. The `()` are parts of the given table item (depending on the boundary declarator position). If you want to apply a special setting for given column, you can do this by `()` followed by column declarator. But if such column is not first, you must use `:()`. Example. We have three centered columns, the second one have to be in bold font and the third one have to be in red: `\table{c:(\bf)c:(\Red)c}{}` \secc Usage of the \code{\\tabskip} primitive The value of `\tabskip` primitive is used between all columns of the table. It is glue-type, so it can be stretchable or shrinkable, see next section~\ref[table.w]. By default, `\tabskip` is 0\,pt. It means that only \^`\tabiteml`, \^`\tabitemr` and `()` can generate visual spaces between columns. But they are not real spaces between columns because they are in fact the part of the total column width. The `\tabskip` value declared before the \^`\table` macro (or in \^`\everytable` or in \^`\thistable`) is used between all columns in the table. This value is equal for all spaces between columns. But you can set each such space individualy if you use `(\tabskip=)` in the immediately before boundary character. The boundary character represents the column pair for which the `\tabskip` have individual value. For example `c(\tabskip=5pt):r` gives `\tabskip` value betwen `c` and `r` columns. You need not to use boundary character explicitly, so `c(\tabskip=5pt)r` gives the same result. The space before first column is given by the \^`\tabskipl` and the space after last column is equal to \^`\tabskipr`. Default values are 0\,pt. Use nonzero `\tabskip` only in special applications. If `\tabskip` is nonzero then horizontal lines generated by \^`\crli`, \^`\crlli` and \^`\crlp` have another behavior than you probably expected: they are interrupted in each `\tabskip` space. \secc[table.w] Tables to given width There are two possibilities how to create tables to given width: \begitems * `\table to{}{}` uses stretchability or shrinkability of all spaces between columns generated by `\tabskip` value and eventually by \^`\tabskipl`, \^`\tabskipr` values. See example below. * `\table pxto{}{}` expands the columns declared by `p{}`, if the `` is given by a virtual \^`\tsize` unit. See example below. \enditems Example of `\table to`: \begtt \catcode`\<=13 \thistable{\tabskip=0pt plus1fil minus1fil} \table to\hsize {lr}{} \endtt % This table has its width `\hsize`. First column starts at the left boundary of this table and it is justified left (to the boundary). Second column ends at the right boundary of the table and it is justified right (to the boundary). The space between them are stretchable and shrinkable in order to reach given width `\hsize`. \medskip Example of `\table pxto` (means \"{\bf p}aragraphs e{\bf x}panded {\bf to}"): \begtt \table pxto\hsize {|c|p{\tsize}|}{\crl aaa & Ddkas jd dsjds ds cgha sfgs dd fddzf dfhz xxz dras ffg hksd kds d sdjds h sd jd dsjds ds cgha sfgs dd fddzf dfhz xxz. \crl bb ddd ggg & Dsjds ds cgha sfgs dd fddzf dfhz xxz ddkas jd dsjds ds cgha sfgs dd fddzf. \crl } \endtt \noindent\table pxto\hsize {|c|p{\tsize}|}{\crl aaa & Ddkas jd dsjds ds cgha sfgs dd fddzf dfhz xxz dras ffg hksd kds d sdjds h sd jd dsjds ds cgha sfgs dd fddzf dfhz xxz. \crl bb ddd ggg & Dsjds ds cgha sfgs dd fddzf dfhz xxz ddkas jd dsjds ds cgha sfgs dd fddzf. \crl } \medskip The first `c` colum is variable width (it gets the width of most wide item) and the resting space to given `\hsize` is filled by the `p` column. You can declare more than one `p{\tsize}` columns in the table when `pxto` keyword is used. The total sum of must be exactly one. For example, \begtt \catcode`\<=13 \table pxto13cm {r p{.3\tsize} p{.5\tsize} p{.2\tsize} l}{} \endtt % This gives the ratio of widths of individual paragraphs in the table. \_endinput 2020-05-20 \colnum implemented. \vspan implemented. 2020-05-19 \fL, \fR ets defined. \table to, \table pxto implemented. 2020-05-10 \bgroup &\egroup added to tabdata (in order to \aftergroup from colors) boundary declarator : introduced \_unsskip: \ifmmode added. 2020-04-10 second group for items in order to \localcolors