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