% macros=mkvi %D \module %D [ file=tabl-xtb, %D version=2011.10.26, %D title=\CONTEXT\ Table Macros, %D subtitle=Xtreme, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. \writestatus{loading}{ConTeXt Table Macros / Xtreme} \registerctxluafile{tabl-xtb}{1.001} % todo: % % - yes or no: foregroundstyle/color <- style/color % - template alignment % - maybe split horizontal (a la linetables) % - before/after and wrapping (linecorrection) % - maybe also some before/after commands % - maybe correction when non float usage % - tagging needs to be checked % - maybe only tag the box % - scale to fit % %D This module started as an afternoon experiment and surprisingly could be %D mostly finished the same evening. Of course it builds upon existing %D functionality. The main reason for writing it is that we occasionally %D run into pretty large tables that take tens of pages and need to be split %D into floats. Speed is one issue there, avoiding to use vsplit is another. %D %D \starttyping %D \definextable [tag] | [tag][parent] %D \setupxtable [settings] | [tag][settings] %D %D \startxtable[tag|settings] %D \startxtablehead|\startxtablenext|\startxtablebody|\startxtablefoot %D \startxrowgroup[tag|settings] %D \startxrow[settings] %D \startxcellgroup[tag|settings] %D \startxcell[settings] ... \stopxcell %D \stopxcellgroup %D \stopxrow %D \startxrowgroup %D \stopxtablehead|\stopxtablenext|\stopxtablebody|\stopxtablefoot %D \stopxtable %D \stoptyping %D %D See xtables-001.tex etc for some examples. % We can avoid some checking by using the fastoptionalcheckcs helpers % instead of dosingleempty but the speed gain is neglectable. \unprotect % \def\v!xtable{xtable} % option=stretch : equal distribution % option={stretch,width} : proportional distribution % option={max} : prefer max over forced width/height % \setbox\scratchbox\hbox attr \taggedattribute \attribute\taggedattribute {...} % % \let\tsplitbeforeresult\donothing % \let\tsplitafterresult \donothing % \let\tsplitinbetween \donothing % \let\tsplitbefore \donothing % \let\tsplitafter \donothing % \let\postprocesstsplit \donothing \let\dotagxtablecell \relax \let\dotagxtablesignal\relax \appendtoks \def\dotagxtablecell {\taggedctxcommand{settagtablecell(\number\tablecellrows,\number\tablecellcolumns,\number\raggedstatus)}}% \def\dotagxtablesignal{\char\zerocount}% not used \to \everyenableelements \newdimen\x_table_width \newdimen\x_table_height \newdimen\x_table_depth % not used \newdimen\x_table_distance \newcount\x_table_nx \newcount\x_table_ny \newcount\x_table_mode \newbox \x_table_box \newcount\x_table_state % 0=empty 1=content 3=splitleft \newdimen\x_table_final_width \newcount\x_table_nesting \newcount\x_table_skip_mode % 1 = skip % \setupxtable[one][parent][a=b,c=d] % \setupxtable[one] [a=b,c=d] % \setupxtable [a=b,c=d] \installframedautocommandhandler \??lt {xtable} \??lt \appendtoks \checkxtableparent % so we can deal with undefined settings, not that it's efficient \to \everysetupxtable \setupxtable[% \c!nr=1, \c!nc=1, \c!nx=1, % slow \c!ny=1, % slow \c!align=\v!table, % {\v!flushleft,\v!broad,\v!high}, % just as \bTABLE .. \eTABLE \c!frameoffset=.5\linewidth, \c!backgroundoffset=\v!frame, % \c!framecolor=\s!black, % \c!foregroundstyle=\xtableparameter\c!style, % not clean, better capture elsewhere % \c!foregroundcolor=\xtableparameter\c!color, % not clean, better capture elsewhere \c!width=\v!fit, \c!height=\v!fit, \c!maxwidth=8em, \c!autowidth=\v!yes, % controls framed \c!rulethickness=\linewidth, \c!strut=\v!yes, \c!autostrut=\v!no, \c!split=\v!auto, % a number will take that many lines \c!splitoffset=\zeropoint, % extra space taken % \c!aligncharacter=\v!no, % \c!alignmentcharacter={,}, % \c!option=, % \v!stretch {\v!stretch,\v!width} % \c!footer=, % \c!header=, \c!spaceinbetween=, \c!textwidth=\hsize, \c!distance=\zeropoint, % individual column \c!columndistance=\zeropoint, % each column (whole table) \c!leftmargindistance=\zeropoint, % whole table \c!rightmargindistance=\zeropoint,% whole table ] \unexpanded\def\startxtable {\dosingleempty\start_x_table} \let\stopxtable\relax \def\x_table_default_buffer{x_table_\number\x_table_nesting} \unexpanded\def\start_x_table[#settings]% maybe two arguments: [tag][settings] | [tag] | [settings] {\bgroup \x_table_prepare{#settings}% \edef\x_table_current_buffer{\x_table_default_buffer}% \dodowithbuffer{\x_table_current_buffer}{startxtable}{stopxtable}\relax\x_table_process} \unexpanded\def\processxtablebuffer {\dosingleempty\process_x_table_buffer} \let\normal_start_x_table\start_x_table \def\process_x_table_buffer[#name]% {\bgroup \let\start_x_table\start_process_x_table_buffer \edef\x_table_current_buffer{#name}% \x_table_process} \def\start_process_x_table_buffer[#settings]% {\x_table_prepare{#settings}% \let\start_x_table\normal_start_x_table} %D We can also define xtables. \appendtoks \setuevalue{\e!start\currentxtable}{\start_named_x_table{\currentxtable}}% \setuevalue{\e!stop \currentxtable}{\stop_named_x_table}% \to \everydefinextable \unexpanded\def\start_named_x_table#tag% {\bgroup \edef\currentxtable{#tag}% \dosingleempty\start_named_x_table_indeed} \unexpanded\def\start_named_x_table_indeed[#settings]% {\advance\x_table_nesting\plusone \dostarttagged\t!table\empty \forgetall \iffirstargument \setupcurrentxtable[#settings]% \fi \edef\x_table_current_buffer{\x_table_default_buffer}% \normalexpanded{\dodowithbuffer{\x_table_current_buffer}{\e!start\currentxtable}{\e!stop\currentxtable}\relax\x_table_process}} \unexpanded\def\stop_named_x_table {} %D Now we come to processing: \def\x_table_prepare#settings% assumes \iffirstargument to be set {\advance\x_table_nesting\plusone \dostarttagged\t!table\empty \forgetall \iffirstargument \set_checked_x_table{#settings}% \fi} \def\x_table_get_buffer {\ctxcommand{gettexbuffer("\x_table_current_buffer")}} \unexpanded\def\x_table_process {\setbox\scratchbox\vbox{\xtableparameter\c!spaceinbetween}% \ctxcommand{x_table_create { option = "\xtableparameter\c!option", textwidth = \number\dimexpr\xtableparameter\c!textwidth, maxwidth = \number\dimexpr\xtableparameter\c!maxwidth, columndistance = \number\dimexpr\xtableparameter\c!columndistance, leftmargindistance = \number\dimexpr\xtableparameter\c!leftmargindistance, rightmargindistance = \number\dimexpr\xtableparameter\c!rightmargindistance, rowdistance = \number\ht\scratchbox, header = "\xtableparameter\c!header", footer = "\xtableparameter\c!footer", } }% \begingroup \let\start_x_row_yes \start_x_row_reflow_width_yes \let\start_x_row_nop \start_x_row_reflow_width_nop \let\stop_x_row \stop_x_row_reflow_width \let\start_x_cell_yes\start_x_cell_reflow_width_yes \let\start_x_cell_nop\start_x_cell_reflow_width_nop \let\stop_x_cell \stop_x_cell_reflow_width \settrialtypesetting \x_table_get_buffer \ctxcommand{x_table_reflow_width()}\relax \endgroup \begingroup \let\start_x_row_yes \start_x_row_reflow_height_yes \let\start_x_row_nop \start_x_row_reflow_height_nop \let\stop_x_row \stop_x_row_reflow_height \let\start_x_cell_yes\start_x_cell_reflow_height_yes \let\start_x_cell_nop\start_x_cell_reflow_height_nop \let\stop_x_cell \stop_x_cell_reflow_height \settrialtypesetting \x_table_get_buffer \ctxcommand{x_table_reflow_height()}\relax \endgroup \begingroup \let\start_x_row_yes \start_x_row_construct_yes \let\start_x_row_nop \start_x_row_construct_nop \let\stop_x_row \stop_x_row_construct \let\start_x_cell_yes\start_x_cell_construct_yes \let\start_x_cell_nop\start_x_cell_construct_nop \let\stop_x_cell \stop_x_cell_construct \x_table_get_buffer \ctxcommand{x_table_construct()}\relax \endgroup \ifinsidesplitfloat \x_table_flush_float_split \else\ifinsidefloat \x_table_flush_float_normal \else \doifelse{\xtableparameter\c!split}\v!yes \x_table_flush_flow_split \x_table_flush_flow_normal \fi\fi \ctxcommand{x_table_cleanup()}% \dostoptagged \resetbuffer[\x_table_current_buffer]% \egroup} \let\extratxtablesplitheight\zeropoint % might disappear so don't depend on it \def\x_table_flush_float_normal {\ctxcommand{x_table_flush{ method = "\v!normal" }}} \def\x_table_flush_float_split {\resetdirecttsplit \edef\extrasplitfloatlines {\xtableparameter\c!split}% \edef\tsplitminimumfreespace{\the\dimexpr\extratxtablesplitheight+\xtableparameter\c!splitoffset\relax}% % \edef\tsplitminimumfreelines{2}% not needed here as we're precise enough \let\tsplitdirectsplitter\x_table_split_splitter \let\tsplitdirectwidth \x_table_final_width \handledirecttsplit} \def\x_table_flush_flow_normal {\ctxcommand{x_table_flush{ method = "\v!normal" }}} \def\x_table_flush_flow_split {\ctxcommand{x_table_flush{ method = "\v!split" }}} \def\x_table_split_splitter#vsize% {\setbox\tsplitresult\vbox {\ctxcommand{x_table_flush{ method = "\v!split", vsize = \number\dimexpr#vsize }}}% \ifcase\x_table_state \global\setfalse\somenextsplitofffloat \else \global\settrue \somenextsplitofffloat \fi} \unexpanded\def\startxrow {\begingroup \doifnextoptionalelse\start_x_row_yes\start_x_row_nop} \def\start_x_row_reflow_width_yes[#settings]% {\setupcurrentxtable[#settings]% \ctxcommand{x_table_next_row()}} \def\start_x_row_reflow_width_nop {\ctxcommand{x_table_next_row()}} \def\stop_x_row_reflow_width {} \let\start_x_row_reflow_height_yes\start_x_row_reflow_width_yes \let\start_x_row_reflow_height_nop\start_x_row_reflow_width_nop \let\stop_x_row_reflow_height \stop_x_row_reflow_width \def\start_x_row_construct_yes[#settings]% {\setupcurrentxtable[#settings]% \dostarttagged\t!tablerow\empty \ctxcommand{x_table_next_row()}} \def\start_x_row_construct_nop {\dostarttagged\t!tablerow\empty \ctxcommand{x_table_next_row()}} \def\stop_x_row_construct {\dostoptagged} \unexpanded\def\stopxrow {\stop_x_row \endgroup} \unexpanded\def\startxcell {\doifnextoptionalelse\start_x_cell_yes\start_x_cell_nop} \unexpanded\def\stopxcell {\stop_x_cell} \def\begin_of_cell {\inhibitblank \everypar{\delayedbegstrut}} \def\end_of_cell {\ifhmode \delayedendstrut \par \else \par \ifdim\prevdepth<\zeropoint % =-1000pt ? \vskip-\strutdp \else \removebottomthings \fi \fi} % For historic reasons we support both nx/nc and ny/nr : maybe nx/ny becomes % obsolete some day. The let as well as the direct speed things up a bit. We % could also consider a \defaultxtableparameter. % % \x_table_nx\defaultxtableparameter\c!nc{\defaultxtableparameter\c!nx\plusone} % \x_table_ny\defaultxtableparameter\c!nr{\defaultxtableparameter\c!ny\plusone} % % Although this becomes kind of messy. It saves already time that we only check % for it when we have settings. \unexpanded\def\start_x_cell_reflow_width_yes[#settings]% {\setbox\x_table_box\hbox\bgroup \ifnum\x_table_nesting>\plusone \letxtableparameter\c!width \v!fit % overloads given width \letxtableparameter\c!height\v!fit % overloads given height \fi % \letxtableparameter\c!nx\plusone \letxtableparameter\c!ny\plusone \letxtableparameter\c!nc\plusone \letxtableparameter\c!nr\plusone % \setupcurrentxtable[#settings]% % \x_table_nx\directxtableparameter\c!nc\relax \x_table_ny\directxtableparameter\c!nr\relax \ifnum\x_table_nx=\plusone \x_table_nx\directxtableparameter\c!nx\relax \fi \ifnum\x_table_ny=\plusone \x_table_ny\directxtableparameter\c!ny\relax \fi % \x_table_distance\xtableparameter\c!distance\relax \ctxcommand{x_table_init_reflow_width()}% \inheritedxtableframed\bgroup \begin_of_cell} \unexpanded\def\start_x_cell_reflow_width_nop {\setbox\x_table_box\hbox\bgroup \ifnum\x_table_nesting>\plusone \letxtableparameter\c!width \v!fit % overloads given width \letxtableparameter\c!height\v!fit % overloads given height \fi \x_table_nx\plusone \x_table_ny\plusone \x_table_distance\xtableparameter\c!distance\relax \ctxcommand{x_table_init_reflow_width()}% \inheritedxtableframed\bgroup \begin_of_cell} \unexpanded\def\stop_x_cell_reflow_width {\end_of_cell \egroup \egroup \ctxcommand{x_table_set_reflow_width()}} \unexpanded\def\start_x_cell_reflow_height_yes[#settings]% {\setbox\x_table_box\hbox\bgroup \ctxcommand{x_table_init_reflow_height()}% \ifcase\x_table_skip_mode % can be sped up \ifnum\x_table_nesting>\plusone \letxtableparameter\c!height\v!fit % overloads given height \fi \setupcurrentxtable[#settings]% \relax \letxtableparameter\c!width\x_table_width % overloads given width \inheritedxtableframed\bgroup \begin_of_cell \fi} \unexpanded\def\start_x_cell_reflow_height_nop {\setbox\x_table_box\hbox\bgroup \ctxcommand{x_table_init_reflow_height()}% \ifcase\x_table_skip_mode % can be sped up \ifnum\x_table_nesting>\plusone \letxtableparameter\c!height\v!fit % overloads given height \fi \relax \letxtableparameter\c!width\x_table_width % overloads given width \inheritedxtableframed\bgroup \begin_of_cell \fi} \unexpanded\def\stop_x_cell_reflow_height {\ifcase\x_table_skip_mode \end_of_cell \egroup \fi \egroup \ctxcommand{x_table_set_reflow_height()}} \unexpanded\def\start_x_cell_construct_yes[#settings]% {\dostarttagged\t!tablecell\empty % can't we just tag the box \setbox\x_table_box\hbox\bgroup \setupcurrentxtable[#settings]% \letxtableparameter\c!width \x_table_width % overloads given width \letxtableparameter\c!height\x_table_height % overloads given height \ctxcommand{x_table_init_construct()}% \inheritedxtableframed\bgroup \begin_of_cell \dotagxtablecell} \unexpanded\def\start_x_cell_construct_nop {\dostarttagged\t!tablecell\empty % can't we just tag the box \setbox\x_table_box\hbox\bgroup \letxtableparameter\c!width \x_table_width % overloads given width \letxtableparameter\c!height\x_table_height % overloads given height \ctxcommand{x_table_init_construct()}% \inheritedxtableframed\bgroup \begin_of_cell \dotagxtablecell} \unexpanded\def\stop_x_cell_construct {\end_of_cell \egroup \egroup \ctxcommand{x_table_set_construct()}% \dostoptagged} \unexpanded\def\startxcellgroup {\begingroup \dosingleempty\start_x_cell_group} \unexpanded\def\stopxcellgroup {\endgroup} \unexpanded\def\start_x_cell_group[#settings]% {\iffirstargument \set_checked_x_table{#settings}% \fi} \unexpanded\def\startxrowgroup {\begingroup \dosingleempty\start_x_row_group} \unexpanded\def\stopxrowgroup {\dostoptagged \endgroup} \unexpanded\def\start_x_row_group[#settings]% {\iffirstargument \set_checked_x_table{#settings}% \fi} % \def\set_checked_x_table#settings % {\doifassignmentelse{#settings} % {\setupcurrentxtable[#settings]} % {\ifcsname\namedxtablehash{#settings}\s!parent\endcsname % \edef\currentxtable{#settings}% % \fi}} \unexpanded\def\set_checked_x_table#settings% {\ifcsname\namedxtablehash{#settings}\s!parent\endcsname \edef\currentxtable{#settings}% \else \setupcurrentxtable[#settings]% \fi} \unexpanded\def\startxtablehead{\begingroup\x_table_mode\plusone \dosingleempty\start_x_table_partition} \unexpanded\def\startxtablefoot{\begingroup\x_table_mode\plustwo \dosingleempty\start_x_table_partition} \unexpanded\def\startxtablenext{\begingroup\x_table_mode\plusthree\dosingleempty\start_x_table_partition} \unexpanded\def\startxtablebody{\begingroup\x_table_mode\plusfour \dosingleempty\start_x_table_partition} \unexpanded\def\start_x_table_partition[#settings]% {\iffirstargument \set_checked_x_table{#settings}% \fi} \unexpanded\def\stop_x_table_partition {\endgroup} \let\stopxtablehead\stop_x_table_partition \let\stopxtablefoot\stop_x_table_partition \let\stopxtablenext\stop_x_table_partition \let\stopxtablebody\stop_x_table_partition %D A bonus: you can use the following construct inside a macro. \unexpanded\def\startembeddedxtable#1\stopembeddedxtable {\ctxcommand{assignbuffer("embedded_x_table",\!!bs\detokenize{#1}\!!es)}% \processxtablebuffer[embedded_x_table]} \let\stopembeddedxtable\relax %D This is an experiment! Beware: you can create loops by using nested %D references to already chained settings. %D %D \startbuffer %D \setupxtable[suffix][align=middle,foregroundcolor=red] %D \setupxtable[blabla][foregroundstyle=slanted] %D \setupxtable[crap] [foregroundcolor=blue] %D \setupxtable[bold] [crap][foregroundstyle=bold] %D %D \startxtable[frame=off] %D \startxtablehead %D \startxrow[bold] %D \startxcell[suffix] a 0 \stopxcell %D \startxcell[blabla] a 1 \stopxcell %D \startxcell a 2 \stopxcell %D \stopxrow %D \stopxtablehead %D \startxtablebody %D \startxrow \startxcell[suffix][ny=2] a 1 \stopxcell \startxcell b 1 \stopxcell \startxcell c 1 \stopxcell \stopxrow %D \startxrow \startxcell b 2 \stopxcell \startxcell c 2 \stopxcell \stopxrow %D \startxrow \startxcell[suffix] a 3 \stopxcell \startxcell b 3 \stopxcell \startxcell c 3 \stopxcell \stopxrow %D \startxrow \startxcell[suffix] a 4 \stopxcell \startxcell b 4 \stopxcell \startxcell c 4 \stopxcell \stopxrow %D \startxrow \startxcell[suffix] a 5 \stopxcell \startxcell b 5 \stopxcell \startxcell c 5 \stopxcell \stopxrow %D \stopxtablebody %D \stopxtable %D \stopbuffer %D %D \typebuffer \placetable{}{\getbuffer} \appendtoks \letvalue{\??lt\currentxtable\s!check}\relax % faster than checking parent \to \everysetupxtable % groups \unexpanded\def\startxgroup {\begingroup \doifnextoptionalelse\start_x_group_delayed_one\relax} \unexpanded\def\stopxgroup {\endgroup} \def\start_x_group_delayed_one[#tag]% % {\ifcsname\namedxtablehash{#tag}\s!parent\endcsname {\ifcsname\??lt#tag\s!check\endcsname \expandafter\start_x_group_delayed_two \else \expandafter\setupcurrentxtable \fi[#tag]} \def\start_x_group_delayed_two[#tag]% {\ifx\currentxtable\empty \else \chaintocurrentxtable{#tag}% \fi \edef\currentxtable{#tag}% \doifnextoptionalelse\setupcurrentxtable\relax} \let\startxrowgroup \startxgroup \let\stopxrowgroup \stopxgroup \let\startxcellgroup\startxgroup \let\stopxcellgroup \stopxgroup % cells (maybe also check for 1 etc but it becomes messy) \unexpanded\def\startxcell {\begingroup \doifnextoptionalelse\start_x_cell_delayed_one\start_x_cell_nop} \def\start_x_cell_delayed_one[#tag]% % {\ifcsname\namedxtablehash{#tag}\s!parent\endcsname {\ifcsname\??lt#tag\s!check\endcsname \expandafter\start_x_cell_delayed_two \else \expandafter\start_x_cell_yes \fi[#tag]} \def\start_x_cell_delayed_two[#tag]% {\ifx\currentxtable\empty \else \chaintocurrentxtable{#tag}% \fi \edef\currentxtable{#tag}% \doifnextoptionalelse\start_x_cell_yes\start_x_cell_nop} \unexpanded\def\stopxcell {\stop_x_cell \endgroup} % rows \unexpanded\def\startxrow {\begingroup \doifnextoptionalelse\start_x_row_delayed_one\start_x_row_nop} \def\start_x_row_delayed_one[#tag]% % {\ifcsname\namedxtablehash{#tag}\s!parent\endcsname {\ifcsname\??lt#tag\s!check\endcsname \expandafter\start_x_row_delayed_two \else \expandafter\start_x_row_yes \fi[#tag]} \def\start_x_row_delayed_two[#tag]% {\ifx\currentxtable\empty \else \chaintocurrentxtable{#tag}% \fi \edef\currentxtable{#tag}% \doifnextoptionalelse\start_x_row_yes\start_x_row_nop} \unexpanded\def\stopxrow {\stop_x_row \endgroup} \protect % \continueifinputfile{tabl-xtb.mkvi}