In LuaLaTeX, this can be implemented similar to lua-ul and luacolor: Use an attribute to mark all text that should be removed, then hook into the shipout routine to delete/replace with empty space:
Create a Lua file hideme.lua with (explanations inline)
local set_func = luatexbase.new_luafunction'hideme.set_attribute'
local reset_func = luatexbase.new_luafunction'hideme.reset_attribute'
local process_func = luatexbase.new_luafunction'hideme.process_attribute'
local functions = lua.get_functions_table()
-- Define the attribute we use as marker
local attr = luatexbase.new_attribute'hide_marker'
-- This function will later activate the hiding. It could be implemented in TeX, but then we would have to make the attribute number available there
functions[set_func] = function()
tex.attribute[attr] = 1
end
functions[reset_func] = function()
tex.attribute[attr] = -0x7FFFFFFF
end
-- Just some shorter names to improve readability and performance
local glue_id = node.id'glue'
local vlist_id = node.id'vlist'
local hlist_id = node.id'hlist'
local whatsit_id = node.id'whatsit'
local rule_id = node.id'rule'
local direct = node.direct
local setglue = direct.setglue
local getid = direct.getid
local todirect = direct.todirect
local getlist = direct.getlist
local setlist = direct.setlist
local getleader = direct.getleader
local traverse = direct.traverse
local free = direct.free
local flush_list = direct.flush_list
local has_attribute = direct.has_attribute
local rangedimensions = direct.rangedimensions
local getprev = direct.getprev
local slide = direct.slide
local node_new = direct.new
local setlink = direct.setlink
local flatten_discretionaries = direct.flatten_discretionaries
-- We later want to remove nodes while we are traversing over them, so add a helper which ensures that deletion gets delayed until we no longer need to look at the node
local delayed_free do
local delayed
function delayed_free(n)
if delayed then free(delayed) end
delayed = n
end
end
local do_vhide
-- Iterate over a horizontal list and hide marked nodes:
local function do_hhide(parent, list)
local work_done, begin_hide
list = flatten_discretionaries(list) -- Nobody likes disc nodes anyway
slide(list) -- Ensure that we can use getprev
for n, id, sub in traverse(list) do
local hide_this
-- We have to recursivly visit vlist and hlist nodes
if id == vlist_id then
do_vhide(n)
elseif id == hlist_id then
setlist(n, (do_hhide(n, getlist(n))))
-- Everything else gets deleted if it is marked
elseif has_attribute(n, attr) then
hide_this = true
if not begin_hide then
-- Actually we don't really delete yet, we only mark for deletion
begin_hide = n
end
-- Again, recursively iterate leaders
elseif id == glue_id and sub >= 100 then -- leaders
local leader = getleader(n)
local leader_id = leader and getid(leader)
if leader_id == hlist_id then
setlist(leader, do_hhide(leader, getlist(leader)))
elseif leader_id == vlist_id then
do_vhide(leader)
end -- else rule --> ignore
end
if not hide_this and begin_hide then
-- Now we have to actually remove the nodes from begin_hide to this point. Let's first measure what we got:
local nglue = node_new(glue_id)
setglue(nglue, rangedimensions(parent, begin_hide, n))
-- Remove n from the list starting at begin_hide
setlink(getprev(n), nil)
-- And integrate nglue in the list without the deleted nodes
if list == begin_hide then
list = setlink(nglue, n)
else
setlink(getprev(begin_hide), nglue, n)
end
-- Now we can delete the list of hidden nodes
flush_list(begin_hide)
begin_hide = nil
work_done = true
end
end
if begin_hide then
-- We end with some hidden nodes. No need for glue here, just delete them
if list == begin_hide then
list = nil
else
setlink(getprev(begin_hide), nil)
end
flush_list(begin_hide)
work_done = true
end
return list, work_done
end
-- In vboxes, the situation is a bit different. It is harder to measure nodes here because rangedimensions doesn't work, but very few node types actually have to be hidden
function do_vhide(parent)
local list = getlist(parent)
for n, id, sub in traverse(list) do
-- Again recurse into the usual suspects (No discretionaries here)
if id == vlist_id then
do_vhide(n)
elseif id == hlist_id then
setlist(n, (do_hhide(n, getlist(n))))
elseif has_attribute(n, attr) then
-- Here we actually remove directly
if id == glue_id and sub >= 100 then -- leaders
-- Just convert them into "regular" glue
flush_list(getleader(n))
direct.setleader(n, nil)
direct.setsubtype(n, 0)
elseif id == rule_id and sub ~= 3 then
-- rules (also includes images etc.) Convert into invisible rules
direct.setsubtype(n, 3) -- empty rule
elseif id == whatsit_id then
-- whatsit - We don't know what they do exactly, so better delete it completly
list = direct.remove(list, n)
delayed_free(n)
end
elseif id == glue_id and sub >= 100 then -- leaders
local leader = getleader(n)
local leader_id = leader and getid(leader)
if leader_id == hlist_id then
setlist(leader, do_hhide(leader, getlist(leader)))
elseif leader_id == vlist_id then
do_vhide(leader)
end -- else rule --> ignore
end
end
setlist(parent, list)
end
-- Now just dome driver to call the function above for a given box
functions[process_func] = function()
local box = todirect(tex.box[token.scan_int()])
local box_id = box and getid(box)
if box_id == hlist_id then
setlist(box, do_hhide(box, getlist(box)))
else
do_vhide(box)
end
delayed_free()
end
-- And give TeX accessible names to our functions
token.set_lua('HideMeStart', set_func, 'global', 'protected')
token.set_lua('HideMeReset', reset_func, 'global', 'protected')
token.set_lua('HideMeProcessBox', process_func, 'global', 'protected')
You can use this from TeX as (based on Ulrike's example)
\documentclass{article}
\usepackage{transparent}
\usepackage{lipsum}
\usepackage{graphicx}
\usepackage{atbegshi}
\directlua{require'hideme'}
\makeatletter
% Don't hide content inserted in the output routine
\output\expandafter\expandafter\expandafter{\expandafter\expandafter\expandafter\HideMeReset\expandafter\@firstofone\the\output}
\makeatother
\AtBeginShipout{\HideMeProcessBox\AtBeginShipoutBox}
\begin{document}
abc
\begingroup
some text [a=b=2] more text
\rule{1cm}{1cm}
\includegraphics[width=5cm]{example-image-duck}
\endgroup
blub
abc
\begingroup\HideMeStart
some text [a=b=2] more text
\rule{1cm}{1cm}
\includegraphics[width=5cm]{example-image-duck}
\endgroup
blub
\end{document}

\tabularasacommand, which goes some way to doing what you want – David Carlisle Jul 30 '20 at 11:28