1

After playing with the answer the question here, I noticed some differences in insert_after and insert_before. node.insert_after adds contents to right margin, node.insert_before doesn't seem to add to left margin. Seems like they both grow as if the contents were placed starting at left margin. The question is how to grow the contents of the hlist n into the left margin (using luatex node library) instead of pushing the main box in right margin.

A not in left margin:

A not in left margin

A in right margin:

A in right margin

In the following code, I have commented 3 different insert recipes. Uncomment them one by one to see the effect. Interestingly Append-2 recipe (insert_before) doesn't produce any result, even though its definition in luatex manual looks like Append-1 recipe (insert_after)


\documentclass{article}
\usepackage{lua-visual-debug}

\directlua{
%
function my_post_lb_filter(h,c)
local cntr=0

for n in node.traverse(h) do
% pull out the boxes and add A
if n.id==0 then
local  newn= node.new(node.id("glyph"))
newn.font=font.current()
newn.char=65 % this is a single character box, for A

% Append-1 works fine, A goes in margin:
% node.insert_after(n.list,node.tail(n.list),newn)

% Append-2 produces no result:
% node.insert_before(n.list,n.head,newn)

% Append-3 instead of putting A in margin makes text violate right margin
% n.head,n=node.insert_before(n.list,n.head,newn)

end
%
end
return h
end
luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'add A at eol')
}



\begin{document}

Vivamus pharetra posuere sapien. Nam consectetuer. 
Sed aliquam, nunc eget euismod ullamcorper, lectus nunc 
ullamcorper orci, fermentum bibendum enim nibh eget ipsum. 
Donec porttitor ligula eu 
dolor. Maecenas 
vitae nulla consequat libero cursus venenatis.

\end{document}


1 Answer 1

4

Both insert_before and insert_after return the new head node as first return value. Technically, this return value is only relevant if the head node changes. (If the head doesn't change, you do not have to change references to the head node in other nodes. If the head node changes, you do.) Given that the head node is the first node of the token list and insert_after inserts a node after another one, insert_after in most cases does not change the head. (There is one exception: If the list was empty, node.insert_after changes the head) On the other hand, if node.insert_before is used to insert something before the head node, the new node will always become the new head and therefore the head will change. So either you always think carefully when using these functions if you might change the head, or you follow a basic rule:

Never ignore the return value of insert_before/insert_after.

So while your Append-1 seems to work fine, it is good practice to change it to

n.head = node.insert_after(n.list, node.tail(n.list), newn)

This avoids ever dealing the issue you had with Append-2.

TeX will always place your nodes starting from the left. (Except if using right-to-left box direction, then everything starts from the right) If you want something to overlap on the left, you can't just add stuff at the beginning, you have to add (negative) glue and/or kern to move it around (similar to how you would do it "normal" TeX). Actually it often makes sense to do the same thing when adding things to the right margin to avoid issues should someone try to repack your boxes. When adding things at the end, the adjustment has to be added after the new nodes (First you add new material, then you go back over it to hide it's width without affecting it's placement). When adding something at the beginning, the adjustment is added before the new nodes (Here we want to affect the placement of the new nodes):

\documentclass{article}
\usepackage{lua-visual-debug}

\directlua{
%
function my_post_lb_filter(h,c)
  local cntr=0

  for n in node.traverse(h) do
    % pull out the boxes and add A
    if n.id==0 then
        local newn= node.new("glyph")
        newn.font=font.current()
        newn.char=65 % this is a single character box, for A
        local newk = node.new("kern")
        newk.kern = -newn.width % A kern which "hides" the width of newn

        % n.head = node.insert_after(n.list,node.tail(n.list),newn)
        % n.head = node.insert_after(n.list,newn,newk)

        n.head=node.insert_before(n.list,n.head,newn)
        n.head=node.insert_before(n.list,newn,newk)
    end
    %
  end
  return h
end
luatexbase.add_to_callback('post_linebreak_filter', my_post_lb_filter, 'add A at eol')
}



\begin{document}

Vivamus pharetra posuere sapien. Nam consectetuer. 
Sed aliquam, nunc eget euismod ullamcorper, lectus nunc 
ullamcorper orci, fermentum bibendum enim nibh eget ipsum. 
Donec porttitor ligula eu 
dolor. Maecenas 
vitae nulla consequat libero cursus venenatis.

\end{document}

enter image description here

3
  • Thanks, this makes things very clear. I wonder when would one need to capture the second return value of insert_before, insert_after? Can these operations really modify the new node that we would have a need to recapture it? From the documentation: <node> head, new = node.insert_after(<node> head, <node> current, <node> new). Also what is the difference between head and current? I see that you specify n.head in place of current, and n.list in place of head. Is it described somewhere in the document?
    – codepoet
    Apr 17, 2020 at 10:31
  • 1
    @reportaman Using n.head and n.list like this has just been copied from your code, both names are aliases. head is always the head, so e.g. n.head if we are currently inside of n, while current is the node before/after which we insert the new node. So insert_before(n.head, n.head, newn) inserts in the beginning, while insert_before(n.head, m, newn) inserts before m` (where m should already be part of the list) Apr 17, 2020 at 10:43
  • 1
    @reportaman You never need to capture the second return value, but sometimes you get to situations where having this return value makes code a bit nicer. (E.g. to overwrite the variable you used as second parameter) Apr 17, 2020 at 10:45

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.