7
$\begingroup$

I'd like to write an ExportString-type function for converting Grid or TextGrid into Latex tables preserving basic style elements including styled text, styled cell dividers, and cell background colorings.

Here's an example of the type of table I'm trying to export. The input would be a styled TextGrid like this:

TextGrid[{{"","x","y","w","z"},{Style["variable 1",Italic],"a","b","c","d"},{Style["variable 2",Bold],"a","b","c","d"},
{Style["variable 3",Blue],"a","b","c","d"},{"variable 4","a","b","c","d"},{"variable 5","a","b","c","d"}},
Background->{{White,{LightGray,White}},{1->LightCyan}},Dividers->{{False,{True},False},
{True,True,{False},True},{{{1,2},{2,-2}}->False,{{1,1},{2,-2}}->True,
{{2,2},{2,-2}}->True}},Alignment->{Center,Center},Spacings->{{1,{3}},1}]

enter image description here

A solution should be able to generate something like the \begin{tablular}...\end{tabular} block in the document below:

\documentclass{article} 
\usepackage{xcolor,colortbl} % (* you may assume these packages *)
\newcommand{\mc}[2]{\multicolumn{#1}{c}{#2}}
\definecolor{Gray}{gray}{0.85}
\definecolor{LightCyan}{rgb}{0.88,1,1}
\definecolor{LightBlue}{rgb}{0,0,1}
\newcolumntype{a}{>{\columncolor{Gray}}c}
\newcolumntype{b}{>{\columncolor{white}}c}
\begin{document} 

    \begin{table} % (* the generated portion *)
    \begin{tabular}{l | a | b | a | b} 
    \hline
    \rowcolor{LightCyan}
    \mc{1}{}  & \mc{1}{x} & \mc{1}{y} & \mc{1}{w} & \mc{1}{z} \\
    \hline
    variable 1 & a & b & c & d \\
    variable 2 & a & b & c & d \\ 
    variable 3 & a & b & c & d \\
    variable 4 & a & b & c & d \\
    variable 5 & a & b & c & d \\ \hline
    \end{tabular}
    \end{table}

\end{document}

Which latex renders beautifully as:

enter image description here

This looks reasonably close to the original TextGrid output above.

$\endgroup$
  • $\begingroup$ Aside: In the mma version, the first and last TextGrid columns look rather cutoff. And sadly it seems like there is no easy fix for that. $\endgroup$ – M.R. Sep 28 at 20:24
  • 1
    $\begingroup$ To avoid the cutoff, replace False in Dividers by Opacity[0]. $\endgroup$ – Jean-Pierre Oct 2 at 0:17
  • $\begingroup$ @Jean-Pierre good call! $\endgroup$ – M.R. Oct 2 at 0:24
  • $\begingroup$ I feel like I'm missing something obvious, @M.R., but how is your LaTeX styling the variable names in the left-most column (personal curiosity)? It's been so long since I've used LaTeX regularly! $\endgroup$ – Josh Bishop Oct 3 at 17:32
3
+300
$\begingroup$

I never used LaTex, so I decided to play with this for a while and use latexbase.com to verify the output. This is not a full solution. It works for the the provided template. Here is what it does:

  • Allows modification of color for all text, backgroud of header row, background of first column and alternating background for subsequent columns. A \definecolor line is issued for all colors and these are named color1, color2...etc.
  • Allows the use of Style[] to enclose text and supports a combination of Bold, Italic, Underlined, and color.
  • Color must be RGBColor or GrayLevel, or the equivalent color name.
  • Spacings applies uniformly for all columns and rows and must be entered in the format {x,y}.
  • Background must be entered in the format shown in the question. The first entry (Yellow in the example) is the background of the first column. Then comes the two colors (LightGray,White) used to alternate color of other columns, and then the rule (1->LightCyan) is the background color of the header row.
  • Text in the first column is aligned left. It is aligned center in the other columns.
  • The Dividersoption was not parsed (i.e. lines were arbitrarily drawn to match the given model in the question).

Here is an example based on the question, but with more variation in styles.

tg = TextGrid[{{"", "x", Style["y", Italic, Bold, Red], 
    Style["w", Underlined], 
    Style["z", RGBColor[1, 0, 1]]}, {Style["variable 1", Italic], "a",
     "b", "c", "d"}, {Style["variable 2", Bold], "a", "b", "c", 
    "d"}, {Style["variable 3", Blue], "a", "b", "c", 
    "d"}, {"variable 4", "a", Style["b", Gray], 
    Style["c", Brown, Bold], "d"}, {"variable 5", "a", "b", "c", 
    Style["d", Blue]}}, 
  Background -> {{Yellow, {LightGray, White}}, {1 -> LightCyan}}, 
  Spacings -> {3, 1.5}, 
  Dividers -> {{Opacity[0], {True}, Opacity[0]}, {True, True, {False},
      True}, {{{1, 2}, {2, -2}} -> False, {{1, 1}, {2, -2}} -> 
      True, {{2, 2}, {2, -2}} -> True}}, 
  Alignment -> {Center, Center}]

(* Conversion function *)
textGridAsTeX[tg_TextGrid] := (
  
  processStyle[s_] := ( Module[{n, m, colorToken, token, colorName},
     If[Length[s] == 0 , Return[s]];
     token = s[[1]];
     n = 2;
     While[n <= Length[s],
      If[Head[s[[n]]] == RGBColor || Head[s[[n]]] == GrayLevel, 
       colorToken = s[[n]]];
      If[s[[n]] == Italic, 
       token = StringJoin["\\textit{", token, "}"]];
      If[s[[n]] == Bold, 
       token = StringJoin["\\textbf{", token, "}"]];
      If[s[[n]] == Underlined, 
       token = StringJoin["\\underline{", token, "}"]];
      n++;
      ];
     
     If[Head[colorToken] == RGBColor || Head[colorToken] == GrayLevel ,
      If[! MemberQ[colorsDefined, ToString[colorToken]],
       colorName = 
        StringJoin["color", ToString[Length[colorsDefined] + 1]];
       AppendTo[colorsDefined, ToString[colorToken]];
       l = Apply[List, colorToken];
       If[Length[l] == 3, colorModel = "}{rgb}", 
        colorModel = "}{gray}"];
       ltex = 
        Insert[ltex, 
         StringJoin["\\definecolor{" , colorName, colorModel, 
          ToString[Apply[List, colorToken]], "\r"], 1],
       p = Flatten[Position[colorsDefined, ToString[colorToken]]];
       colorName = StringJoin["color", ToString[p[[1]]]];
       ];
      
      token = StringJoin["\\textcolor{", colorName, "}{", token, "}" ];
      
      ];
     Return[token];]
    );
  
    cName[col_] := (
    If[! MemberQ[colorsDefined, ToString[col]],
     colorName = 
      StringJoin["color", ToString[Length[colorsDefined] + 1]];
     AppendTo[colorsDefined, ToString[col]];
     l = Apply[List, col];
     If[Length[l] == 3, colorModel = "}{rgb}", colorModel = "}{gray}"];
     ltex = 
      Insert[ltex, 
       StringJoin["\\definecolor{" , colorName, colorModel, 
        ToString[l], "\r"], 1],
     
     p = Flatten[Position[colorsDefined, ToString[col]]];
     colorName = StringJoin["color", ToString[p[[1]]]];
     ];
    colorName
    );
  
  colorsDefined = {};
  tgList = Apply[List, tg];
  rows = Length[tgList[[1]]];
  cols = Length[tgList[[1]][[1]]];
  ltex = {};
  
  (* spacing *)
  
  spacing = 
   FilterRules[tgList[[2 ;; Length[tgList]]], Spacings][[1]][[2]];
  v = spacing[[1]];
  AppendTo[ltex, 
   StringJoin["\\setlength{\\tabcolsep}{", ToString[v*6], "pt}\r"]];
  v = spacing[[2]];
  AppendTo[ltex, 
   StringJoin["\\renewcommand{\\arraystretch}{", ToString[v], 
    "}\r"]];
  
  (* new column types *)
  
  bck = FilterRules[tgList[[2 ;; Length[tgList]]], Background];
  newcol = bck[[1]][[2]][[1]][[2]];
  
  n = 1;
  While[n <= Length[newcol],
   AppendTo[ltex, 
    StringJoin["\\newcolumntype{", {"a", "b"}[[n]], 
     "}{>{\\columncolor{" , cName[newcol[[n]]], "}}c}\r"]];
   n++;
   ];
  newCol = bck[[1]][[2]][[1]][[1]];
  AppendTo[ltex, 
   StringJoin["\\newcolumntype{", "d", "}{>{\\columncolor{" , 
    cName[newCol], "}}l}\r"]];
  
  (* new command *)
  
  AppendTo[ltex, 
   "\\newcommand{\\mc}[2]{\\multicolumn{#1}{c}{#2}}\r"];
  
  (* begin document *)
  AppendTo[ltex, "\\begin{document}\r"];
  
  (* the table *)
  AppendTo[ltex, "\\begin{table}\r"];
  
        (* tabular *)
    AppendTo[ltex, "\\begin{tabular}"];
  
  (* columns description *)
  aCols = Table["a", cols];
  bCols = Table["b", cols];
  rCols = Riffle[aCols, bCols];
  rfCols = Take[rCols, cols - 1];
  colTypes = {"{d"};
  n = 1;
  While[n <= cols - 1,
   AppendTo[colTypes, StringJoin[" | ", rfCols[[n]]]];
   n++;
   ];
  AppendTo[ltex, StringJoin[colTypes, "}\r"]];
  
  (* Header row *)
  AppendTo[ltex, "\\hline\r"];
  rowNumber = bck[[1]][[2]][[2]][[1]][[1]];
  rowVal = cName[bck[[1]][[2]][[2]][[1]][[2]]];
  AppendTo[ltex, StringJoin["\\rowcolor{", rowVal, "}\r"]];
  cList = tgList[[1]][[1]];
  n = 1;
  While[n <= cols,
   AppendTo[ltex, 
    StringJoin["\\mc{1}{", processStyle[cList[[n]]], "}"]];
   If[n == cols, AppendTo[ltex, " \\\\\r"], AppendTo[ltex, " & \r"]];
   n++;
   ];
  AppendTo[ltex, "\\hline\r"];
  
  (* Rows *)
  m = 2;
  While[m <= rows,
   cList = tgList[[1]][[m]];
   n = 1;
   While[n <= cols,
    AppendTo[ltex, processStyle[cList[[n]]]];
    If[n == cols, AppendTo[ltex, " \\\\\r"], AppendTo[ltex, " & "]];
    n++;
    ];
   m++;
   ];
  AppendTo[ltex, "\\hline\r"];
  
  (* End  tabular*)
  AppendTo[ltex, "\\end{tabular}\r"];
  
  (* end table *)
  AppendTo[ltex, "\\end{table}\r"];
  
  (* end document *)
  AppendTo[ltex, "\\end{document}\r"];
  
  (* package *)
  ltex = Insert[ltex, "\\usepackage{colortbl}\r", 1];
  
  (* the documentclass *)
  
  ltex = Insert[ltex, "\\documentclass{article}\r", 1];
  
  
  StringJoin[ltex]
  ) 

(* calling the function *)
textGridAsTeX[tg]

This is the result: enter image description here

Mathematica:

enter image description here

LaTex:

enter image description here

| improve this answer | |
$\endgroup$
  • $\begingroup$ It is very good (+1). It looks like you are not using TeXForm[] and ExportString[..., "TeX"]. Why? $\endgroup$ – Alex Trounev Oct 3 at 19:18
  • 1
    $\begingroup$ No particular reason. It would likely be more efficient. I guess that since I am not familiar with the details of LaTex (I just read about the format on the web in the last few days) and related Mathematica functions, I tried to do the conversion from scratch. $\endgroup$ – Jean-Pierre Oct 3 at 19:30
  • $\begingroup$ @AlexTrounev because TeXForm and Export[..., “TeX”] generally do not work (they are ancient functionalities that have not been updated) and most certainly do not support styles - which is the main part of the question $\endgroup$ – M.R. Oct 3 at 19:32
  • $\begingroup$ @M.R. I understand the limitations of Mathematica, but direct translation from input code to output code also looks funny:) $\endgroup$ – Alex Trounev Oct 3 at 19:44
  • 1
    $\begingroup$ Please add a new answer if you can think of another approach! $\endgroup$ – M.R. Oct 3 at 19:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.