184 TUGboat, Volume 31 (2010), No. 3 Three things you can do with LuaTEX that a horizontal list, what you get is the first node of would be extremely painful otherwise that list; you access the rest by sliding from next to next. Paul Isambert Nodes also have several other fields, depending on their types. These types are recorded as a Introduction number in their id field, a numeric value. For LuaTEX has made some typographic operations so instance, a glue node has id 10, whereas a glyph easy one might wonder why it wasn't invented thirty node has id 37. As long as LuaTEX hasn't reached years ago (probably because Lua didn't exist then). version 1, though, such values might change. So, in Here I'm going to describe three simple features order for our code to last, we must use the following that would require advanced wizardry to do the workaround: the node.id() function, when fed a same with any other engine. LuaTEX allows you to string denoting a node type, returns the associated explore some of TEX's most intimate parts with a id number. For instance, node.id("glue") returns rather easy programming language, and the result 10. Thus, when using symbolic names, we can is you can quite readily access things that were get the right id value, regardless of changes in unreachable before. The three issues I'm going to versions of LuaTEX. Another important field for address are: nodes is subtype, which distinguishes between nodes with the same id. It's a numeric value, and for • Turning lines into rules whose color depends on whatsits (which are numerous), one should use the line's original stretch or shrink. node.subtype() like node.id(). • Underlining. Symbolic names won't change; they are listed • Margin notes that align properly with the text. in the LuaTEX reference manual, in the chap- I'll try to explain some of LuaTEX's basic ter called Nodes, available from the LuaTEX web functionality as we encounter these issues, but two site; they're also listed in the tables returned by of them are worth mentioning right now: callbacks node.types() and node.whatsits(). It's simpler to and nodes. define variables beforehand rather than call node.id First, we can control TEX's operations at and node.subtype each time we need them. That's various stages thanks to callbacks. These are points what we'll do here: the following declarations should at which we can insert Lua code to modify or start any file containing our code; it can also be enhance TEX's processing. Callbacks range from made global by removing the local prefix and thus processing TEX's input buffer (e.g. to accommodate used anywhere once declared, but local variables a special encoding) to rewriting the paragraph are faster and safer. I use uppercase to mark their builder and loading OpenType fonts. status. Second, we can manipulate lists of nodes. To local HLIST = node.id("hlist") put it simply, nodes are the atoms that TEX local RULE = node.id("rule") uses to create pages: boxes, glyphs, glues, but also local GLUE = node.id("glue") penalties, whatsits, etc. A list of nodes is a sequence local KERN = node.id("kern") of such atoms linked together. A simple paragraph, local WHAT = node.id("whatsit") for instance, is a list made of horizontal boxes (the local COL = node.subtype("pdf_colorstack") lines), penalties and glues. The boxes themselves are lists containing mostly glyph and glue nodes. The color of a page Nodes are linked together like beads on a string, and Typographers speak of a page's color. While the the prev field of a node points to the preceding node color itself depends on several factors, its evenness in the list, whereas the next field returns the one depends on how lines are justified: loose lines make that follows (there is an understandable exception the page uneven in color, because large interword for the first and last nodes of a list, whose prev and space creates holes in the overall greyness. last fields respectively return nil). An important The code that follows takes the metaphor point to keep in mind is that when you query the literally: it turns a page's color into a real color content of, say, an \hbox, which in TEX's internal is pattern. The idea is to replace each line with a rule of the same height and width, and whose color Author's note: I'm not a member of the LuaTEX team and this paper has no kind of official authority | it's just the depends on the line's badness. If we take 0 as black result of experimentation by a LuaTEX user. Any error or and 1 as white, then a good line gets .5, tight lines misconception is mine. approach 0 (which represents an overfull line) and TUGboat, Volume 31 (2010), No. 3 185 loose lines tend to 1 (an underfull line). Now we don't allow more than 1 in order to remain in the have paragraphs and pages made of grey bars; the color range). less contrast between them, the better the page. The last line sets the color of the line as the To do this, we retrieve the horizontal boxes code to color_push, i.e. `n g', where n is a number created by the paragraph builder, check the badness between 0 and 1 and g a PDF operator setting the of each, then replace the box with the desired color in the grey model. In the rest of the loop we rule. This is easy to do in LuaTEX: we register replace the line's content with a sequence of three a function in the post_linebreak_filter callback. nodes: color_push, a rule, and color_pop: This callback accesses the list of nodes output local rule = node.new(RULE) by the paragraph builder, i.e. the lines of text rule.width = line.width interspersed with interline penalties and glues, plus local p = line.list perhaps other things (whatsits, inserts, :::) that line.list = node.copy(color_push) we'll ignore. Among these nodes we retrieve the node.flush_list(p) ones we want, namely the lines of text, and replace node.insert_after(line.list, them as described. line.list, rule) The code that follows, as all Lua code, should node.insert_after(line.list, node.tail(line.list), be fed to \directlua or stored in a .lua file. node.copy(color_pop)) local color_push = node.new(WHAT, COL) end local color_pop = node.new(WHAT, COL) color_push.stack = 0 What is done here is: first, we create a rule whose color_pop.stack = 0 width is the same as the original line's (we could color_push.cmd = 1 have created this rule beforehand with a width color_pop.cmd = 2 equal to \hsize, but this way we accommodate Here we have created two new whatsit nodes iden- changing line widths). Then we set the line's list as tified by their subtype as the Lua equivalents of a copy of color_push (we use a copy since we need \pdfcolorstack. They both modify stack 0 and that node for each line), and then we insert the rule color_push adds code to the stack while color_pop node and a copy of color_pop. The first argument removes it. We'll use them to set the color of each to node.insert_after is the list (denoted by its first line, with the exact content of the code added by node!) where we perform the insertion, the second color_push to be specified each time. one is the node in that list after which the insertion textcolor = function (head) is performed, and the last one is the inserted node; for line in node.traverse_id(HLIST, head) do node.tail returns the last node of its argument, so local glue_ratio = 0 the third node.insert_after inserts at the end of if line.glue_order == 0 then the list. if line.glue_sign == 1 then The story with p is this: we retrieve the line's glue_ratio = .5 * math.min(line.glue_set, content before replacing it, so we can erase it from 1) T X's memory; it has no effect on the output. else E glue_ratio = -.5 * line.glue_set Finally, and most importantly, we return the end mutated list for TEX to continue its operations, and end close the function. color_push.data = .5 + glue_ratio .. " g" return head Here's the beginning of our main function. It takes a end node as its argument: it will be the first node of the Now, to use the function, we register it in the list returned by the paragraph builder. That node, post_linebreak_filter callback: remember, denotes the entire list. We retrieve each line of text in this list, i.e. each node with id HLIST, \directlua{% and check its glue_order field; if it is 0, then the callback.register("post_linebreak_filter", line has been justified with finite glue and we want textcolor)} to know how bad it is (if the line uses infinite glue Note that we could improve this code for the then it is good by definition, as far as glue setting is first and last lines of a paragraph, taking the concerned). We access glue_sign to know whether indent and \parfillskip into account to create stretching or shrinking was used and glue_set to more faithful images of those lines. I leave it as an know the ratio (1 means the stretch/shrink was exercise to the reader, as is customary. fully used; glues can also be overstretched, but we 186 TUGboat, Volume 31 (2010), No.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages7 Page
-
File Size-