Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

In LuaLaTeX, how would I go about capturing the content of an environment for (verbatim) processing by Lua? For example, consider something like

\begin{foobar}
  Hello {World}
\end{foobar}

I'd like to be able to take the content of the environment and pass it to Lua, as in

foobar("  Hello {World}\n")

(and Lua would then eventually pass the processed version back to LaTeX, but I can presumably handle that).

share|improve this question
2  
If you think that one of the answers solves your problem, you should accept it. If it doesn't, it would be nice to hear what could be improved. –  topskip Dec 16 '11 at 13:04

3 Answers 3

Start the "recording" mode when you enter \begin{foobar} and end it when you close the environment. That way you get the pure buffer contents.

\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
do 
  local mybuf = ""
  function readbuf( buf )
    mybuf = mybuf .. buf .. "\n" 
  end
  function startrecording()
    luatexbase.add_to_callback('process_input_buffer', readbuf, 'readbuf')
  end

  function stoprecording()
    luatexbase.remove_from_callback('process_input_buffer', 'readbuf')
    local buf_without_end = mybuf:gsub("\\end{foobar}\n","")
    print(string.format("Lua: %q", buf_without_end))
  end
end
\end{luacode*}
\begin{document}
  \newenvironment{foobar}{\directlua{startrecording()}}{\directlua{stoprecording()}}
\begin{foobar}
    Hello {World}
\end{foobar}

\end{document}
share|improve this answer
1  
Amazing! Why do you include the lua code inside a closure do...end? Just best practice in lua coding? –  cjorssen Dec 14 '11 at 11:29
    
@cjorssen I define a local variable mybuf (which act as a kind of global variable for these two functions) and I don't want it to be visible outside the scope of the functions where I use it (to avoid clashes). In this case it is actually not necessary as the do / end pair is implicitly given by the luacode environment, but I like the explicit scoping here so anyone can see "hey, there is something inside that scope that must not be visible outside". –  topskip Dec 14 '11 at 11:44
    
In your example: Is it also possible to suppress the TeX output of the contents of {foobar}? I.e. read the environement contents to Lua, but hide it from TeX? –  arney Mar 24 '13 at 23:55
    
@arney I am not sure (didn't try) - did you try to return nil or an empty string from readbuf()? –  topskip Mar 25 '13 at 7:15

In ConTeXt, you can simply use buffers for this.

\def\startfoobar
    {\definebuffer[foobar][startfoobar][stopfoobar]}

\def\stopfoobar
    {\ctxlua{userdata.luafunction(buffers.getcontents('foobar'))}

The function userdata.luafunction will then receive the contents of the environment.

See the ConTeXt wiki for more examples.

share|improve this answer

A bit late to the party, but this fix to http://tex.stackexchange.com/users/243/topskip's answer does the job:

in response to question in comments:

In topskip's answer Hello World is still written to the pdf file, which is not typically what one would want. So I've taken his answer and made the change that if the line read does not contain "\end{foobar}" then nothing is returned to the tex interpreter, but if it does contain "\end{foobar}" it gets spit back (via the nil return value) to the tex interpreter which then ends the foobar environment. If the "\end{foobar}" line would not get returned then tex would never end the environment, which is clearly not what you want.

The example is still imperfect in that it doesn't give the desired result if there is text before "\end{foobar}" on the same line.

\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
do 
  local mybuf = ""
  function readbuf( buf )
   print("buf=" .. buf)
    i,j = string.find(buf,"\\end{foobar}")
    print(i,j)
    if i==nil then
     mybuf = mybuf .. buf .. "\n" 
     print("[" .. mybuf .. "]")
     return ""
    else 
    return nil
    end
   end

  function startrecording()
    luatexbase.add_to_callback('process_input_buffer', readbuf, 'readbuf')
  end

  function stoprecording()
    luatexbase.remove_from_callback('process_input_buffer', 'readbuf')
    local buf_without_end = mybuf:gsub("\\end{foobar}\n","")
    print(string.format("Lua: %s", buf_without_end))
  end
end
\end{luacode*}

\begin{document}
  \newenvironment{foobar}{\directlua{startrecording()}}{\directlua{stoprecording()}}
\begin{foobar}
    Hello {World}
\end{foobar}

\end{document}
share|improve this answer
2  
Just out of curiosity, would you mind providing a little explanation of how your answer differs materially from the one of @topskip? –  Mico Jul 14 at 4:46
    
done, thank you –  JPi Jul 14 at 18:39

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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