Hello, in the following PlainTeX example, I try to access all \hboxes created by TeX by using the hpack_filter callback: --------------------------------------------------------------- %&luatex \directlua0{ callback.register('hpack_filter', function(head, group, size, ptype) print() print(group, size / 65536, ptype) return true end) } % From Wikipedia: Beneath a Steel Sky is a British 1994 science fiction point and click adventure game in the cyberpunk genre. It featured comedy elements and was developed by Revolution Software and published by Virgin Interactive Entertainment. It was initially released for DOS and Amiga. Underworld was its working title. The game was the second to use Revolution Software's Virtual Theatre engine, the first being Lure of the Temptress. The game's backgrounds and introduction sequence were designed by Dave Gibbons. The introduction sequence was also included as a separate promotional comic book in some releases of the game. The game is considered to be among the true classics in the early era of graphic adventure games for the early 1990s. This, in part, is related to the mature science fiction theme that was chosen for this game as compared to other games of the same era where fantasy themes (a la King's Quest) had been dominant. Some critics have attributed this difference to the different visions of the adventure game genre between American and European developers. \bye This results in: This is LuaTeX, Version snapshot-0.25.2-2008041019 (Web2C 7.5.6) (HPackFilter.tex adjusted_hbox 469.75498962402 exactly adjusted_hbox 469.75498962402 exactly [1] ) Output written on HPackFilter.dvi (1 page, 2152 bytes). Transcript written on HPackFilter.log. --------------------------------------------------------------- Why does the filter not get called more often, namely once for every line in the output file? Background: I want to calculate the width of the last line of a paragraph so I can adjust spacing following this paragraph accordingly (a bit like display math does it). I tried the buildpage_filter but did not know how to calculate the width of the last \hbox in the list without the \parfillskip glue at the end. Which leads me to another question: When having a \hbox node list, how to I determine the horizontal (when typesetting horizontally) resp. vertical (when typesetting vertically) position of a node relative to the left/top edge of the \hbox? I would think that after line breaking, each node should know this position, but how do I access it? Jonathan
Jonathan Sauer wrote:
Hello,
in the following PlainTeX example, I try to access all \hboxes created by TeX by using the hpack_filter callback:
--------------------------------------------------------------- [deleted] ---------------------------------------------------------------
Why does the filter not get called more often, namely once for every line in the output file?
It only gets called for explicit hboxing (that is an optimization, but it is really needed). The manual should be clearer on this, sorry about that. The hpack_filter is not the right callback, at least not for now. I could make a dedicated callback for line packaging, maybe? It is possible to get the "prepared paragraph" using the "post_linebreak_filter", but then you have a bit of work to do. Here is the code: \directlua0{ callback.register('post_linebreak_filter', function(head) % head is the alternating list of hboxes and glues etc. for i in node.traverse_id('hlist',head) do if i.next == nil then % last line has no next v = node.hpack(i.list,'natural') print (v.width / 65535) % next two lines prevent memory leaks v.list = nil node.free(v) end end return true end) } If you want to be 'perfect', you should copy i.list to and remove the last glue(s) from the copy before hpacking() it: the final glue(s) may have a non-zero natural width.
Which leads me to another question: When having a \hbox node list, how to I determine the horizontal (when typesetting horizontally) resp. vertical (when typesetting vertically) position of a node relative to the left/top edge of the \hbox? I would think that after line breaking, each node should know this position, but how do I access it?
Using the function above, you can calculate the position within a paragraph by adding up the various widths and heights and depths. Exact positions are trickier: Nothing is certain until after \output, so using the normal position primitives (\pdfsavepos, \pdflastxpos etc) or \latelua0 {print(pdf.h)} and a two-pass approach is probably easier to deal with. Best wishes, Taco
Hello,
The hpack_filter is not the right callback, at least not for now. I could make a dedicated callback for line packaging, maybe?
It is possible to get the "prepared paragraph" using the "post_linebreak_filter", but then you have a bit of work to do. Here is the code:
Thank you! A few questions, though:
\directlua0{ callback.register('post_linebreak_filter', function(head) % head is the alternating list of hboxes and glues etc. for i in node.traverse_id('hlist',head) do if i.next == nil then % last line has no next v = node.hpack(i.list,'natural')
"v" should be local, of course. The two-parameter form of node.hpack is a bit strange (since not explained in the manual). Is above line different from "v = node.hpack(i.list)"? (Incidentally, the manual also does not note that node.traverse_id can take a string as its first parameter.)
print (v.width / 65535) % next two lines prevent memory leaks v.list = nil
I don't quite understand why this is necessary. Since node.hpack creates a new node list, why should this list not be freed by node.free?
node.free(v) end end return true end) }
If you want to be 'perfect', you should copy i.list to and remove the last glue(s) from the copy before hpacking() it: the final glue(s) may have a non-zero natural width.
I'll call that a feature ;-)
Which leads me to another question: When having a \hbox node list, how to I determine the horizontal (when typesetting horizontally) resp. vertical (when typesetting vertically) position of a node relative to the left/top edge of the \hbox? I would think that after line breaking, each node should know this position, but how do I access it?
Using the function above, you can calculate the position within a paragraph by adding up the various widths and heights and depths.
Umm ... in the case of a glyph node, I then would access the font using font.fonts[glyph.font] and then retrieve the character's dimensions from the appropriate table? Since as far as I can see, not all nodes store their dimension directly.
Best wishes, Taco
Thanks in advance, Jonathan
Jonathan Sauer wrote:
\directlua0{ callback.register('post_linebreak_filter', function(head) % head is the alternating list of hboxes and glues etc. for i in node.traverse_id('hlist',head) do if i.next == nil then % last line has no next v = node.hpack(i.list,'natural')
"v" should be local, of course.
Yes, sure. I was rather sloppy in the lua, I only tried what worked instead of verifying the actual code.
The two-parameter form of node.hpack is a bit strange (since not explained in the manual). Is above line different from "v = node.hpack(i.list)"?
No. In fact, my code was wrong, there is no two-argument call possible (but in this case, that was silently ignored).
(Incidentally, the manual also does not note that node.traverse_id can take a string as its first parameter.)
Same kind of error here. The code only works because tonumber("hlist") == 0, and that is the same as the type id of node type hlist. Coincidence.
print (v.width / 65535) % next two lines prevent memory leaks v.list = nil
I don't quite understand why this is necessary. Since node.hpack creates a new node list, why should this list not be freed by node.free?
Because it is also still present in the paragraph. If you do not clear the list item, you will get a double-free error later on in the best case scenario. In the worst case the nodes have been reallocated already, and the paragraph contains garbage, potentially resulting in a crash. A cleaner solution here would have been to hpack a copy, but that is a lot less efficient.
Using the function above, you can calculate the position within a paragraph by adding up the various widths and heights and depths.
Umm ... in the case of a glyph node, I then would access the font using font.fonts[glyph.font] and then retrieve the character's dimensions from the appropriate table? Since as far as I can see, not all nodes store their dimension directly.
Either that, or you can hpack() the line-so-far. But in both cases, you also have to compensate for the glue setting, so the solution with \pdfsavepos c.s. is much easier in practice. Best wishes, Taco
Hello,
print (v.width / 65535) % next two lines prevent memory leaks v.list = nil
I don't quite understand why this is necessary. Since node.hpack creates a new node list, why should this list not be freed by node.free?
Because it is also still present in the paragraph. If you do not clear the list item, you will get a double-free error later on in the best case scenario. In the worst case the nodes have been reallocated already, and the paragraph contains garbage, potentially resulting in a crash.
Ah, now I understand: node.hpack creates a new node list that *contains* the original node list. Maybe this could be noted in the manual: "Note that the new hlist still references the original node list, so if one wishes to only free the new hlist, the following code must be used to avoid deallocating the original list as well: hlist.list = nil node.free(hlist)
Best wishes, Taco
Jonathan
Jonathan Sauer wrote:
Ah, now I understand: node.hpack creates a new node list that *contains* the original node list.
Maybe this could be noted in the manual: "Note that the new hlist still references the original node list, so if one wishes to only free the new hlist, the following code must be used to avoid deallocating the original list as well:
Still planned: a conversion from the current explicit node (de)allocation system to automatic garbage collection. Until then, a remark in the manual is a good idea. Best wishes, Taco
participants (2)
-
Jonathan Sauer
-
Taco Hoekwater