3

I need a macro to be able, in pdflatex (EDIT : or lualatex if necessary), to remove some text/formulas/images which can go on many paragraphs and pages, without changing the layout (there must be an empty space with the exact same size at its place).

This would work like \phantom but, as an environment and accepting many paragraphs, page breaks etc... I thought it already exists but I can't find any solution (it could be a good idea to have more general \phantomplus and phantomplusenv macros - idea for a phantomplus package ? ;-) ).

For now, I simply use a \color{white} to hide text|formulas but it's not sufficient (text is still in the pdf, is selectable etc...).

I saw How to replace a large block of text by an empty block of the same size? but there is no available solution in it for my situation.

If it is a good and realistic idea, how to do this ? Or other idea (with lualatex ?) ?

6
  • you have tagged this pdftex but it's probably easier in luatex if that is an option. Commented Jul 29, 2020 at 18:07
  • any pdftex version is going to have lots of constraints in the allowed content and as seen in the answer posted so far probably can't really remove the text just hide it a bit Commented Jul 29, 2020 at 21:18
  • @David Well, I tried (for my first time...) lualatex and it seems to work well with all of my documents without a big rework... So I think I can switch to lualatex if necessary. So if you have a solution to my problem using lualatex, I can accept it. Commented Jul 30, 2020 at 8:30
  • How well must the content be hidden? It is enough if is simply not visible, or should also copy&paste not show the content? Commented Jul 30, 2020 at 9:33
  • 3
    You should look at the chickenize package (a very important luatex package) and its \tabularasa command, which goes some way to doing what you want Commented Jul 30, 2020 at 11:28

3 Answers 3

8

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} 

enter image description here

0
2

A multi-line, single-page workaround is to use \pgfsys@begininvisible and \pgfsys@endinvisible provided by pgf package, the backend of tikz.

Contents in between these two commands are actually typeset but with a large amount of shift (x = 20000bp, y = 20000bp). The overlay utility in beamer class uses this pair of commands as well.

\documentclass{article} \usepackage{lipsum} \usepackage{pgf} \begin{document} \makeatletter \lipsum[1] \pgfsys@begininvisible \lipsum[2] % the output of this line is moved out of the page \pgfsys@endinvisible \lipsum[3] \makeatother \end{document} 
5
  • I tried and pdftotext doesn't see the hidden text. Commented Jul 29, 2020 at 18:58
  • @egreg With \pdfcompresslevel=0 \pdfobjcompresslevel=0, you can check the hidden text is stored in pdf, although pdftotext can still see only two paragraphs. Commented Jul 29, 2020 at 19:11
  • 1
    I feared that, which makes the trick a bit less valuable. Noobs will be baffled, experts… Commented Jul 29, 2020 at 19:14
  • This solution doesn't work when there is a page break (like you said it). Commented Jul 29, 2020 at 21:13
  • HOw to improve it to support page-break? Commented Jun 20, 2021 at 8:48
2

You could use the transparent package. But it will make the content only transparent, it is still there, e.g. for copy&paste:

\documentclass{article} \usepackage{transparent} \usepackage{lipsum} \usepackage{graphicx} \begin{document} abc \begingroup some text \[a=b=2\] more text \rule{1cm}{1cm} \includegraphics[width=5cm]{example-image-duck} \endgroup blub abc \begingroup\transparent{0} some text \[a=b=2\] more text \rule{1cm}{1cm} \includegraphics[width=5cm]{example-image-duck} \endgroup blub \end{document} 

enter image description here

1
  • As you said it, copy/paste still works here. It must not work. All content must be removed. Commented Jul 30, 2020 at 10:56

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.