summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/mk/mk-itworks.tex
blob: 0a9d5fce8cf06c02e849bb546da231b8ecc279fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
% language=uk

\environment mk-environment

\startcomponent mk-itworks

\chapter{It works!}

One of the more powerful commands in \CONTEXT\ is \type {\framed}.
You can pass quite some parameters that control the spacing,
alignment, backgrounds and more. This command is used all over the
place (although often hidden for the user) which means that it also has
to be quite stable. However, there is one nasty bit of code that
is hard to get right. Calculating the height of a box is not that
complex: the height that \TEX\ reports is indeed the height.
However, the width of box is determined by the value of \type
{\hsize} at the time of typesetting. The actual content can be
smaller. In the \type {\framed} macro by default the width is
calculated automatically.

\startbuffer
\framed
  [align=middle,width=fit]
  {Out beyond the ethernet the spectrum spreads \unknown}
\stopbuffer

\typebuffer

this shows up as:\footnote{Taken from \quote {Casino Nation} by Jackson Browne.}

\startlinecorrection
\getbuffer
\stoplinecorrection

Or take this quote:\footnote{Taken from \quote {A World Without Us} by Alan Weisman.}

\startbuffer
\hsize=.6\hsize \framed [align=middle,width=fit] {\input weisman }
\stopbuffer

\typebuffer

This gives a multi|-|line paragraph:

\startlinecorrection
\getbuffer
\stoplinecorrection

Here the outer \type {\hsize} was made a bit smaller. As you can
see the frame is determined by the widest line. Because it was one
of the first features we needed, the code in \CONTEXT\ that is
involved in determining the maximum natural width is pretty old.
It boils down to unboxing a \type {\vbox} and stepwise grabbing
the last box, penalty, kern and skip. You unwind the box
backwards. However, you cannot grab everything or in \TEX\ speak:
there is only a limited number of \type {\lastsomething} commands.
Special nodes, like whatsits cannot be grabbed and they make the
analyzer abort its analysis. There is no way that we can solve
this in traditional \TEX\ and in \CONTEXT\ \MKII.

So how about \LUATEX\ and \CONTEXT\ \MKIV ? The macro used in the
\type {\framed} commands is:

\starttyping
\doreshapeframedbox{do something with \box\framebox}
\stoptyping

In \LUATEX\ we can manipulate box content at the \LUA\ level. Instead
of providing a truckload of extra primitives (which would also introduce
new data types at the \TEX\ end) we just delegate the job to \LUA.

\starttyping
\def\doreshapeframedbox
  {\ctxlua{commands.doreshapeframedbox(\number\framebox)}}
\stoptyping

Here \type {\ctxlua} is our reserved instance and \type {commands}
provides the namespace for commands that we delegate to \LUA\ (so,
there are more of them). The amount of \LUA\ code is way less than
the \TEX\ code which we will not show here; it's in \type
{supp-box.tex} if you really want to see it.

\starttyping
function commands.doreshapeframedbox(n)
    local box_n = tex.box[n]
    if box_n.width ~= 0 then
        local hpack = node.hpack
        local free = node.free
        local copy = node.copy_list
        local noflines, lastlinelength, width = 0, 0, 0
        local list = box_n.list
        local done = false
        for h in node.traverse_id('hlist',list) do
            done = true
            local p = hpack(copy(h.list))
            lastlinelength = p.width
            if lastlinelength > width then
                width = lastlinelength
            end
            free(p)
        end
        if done then
            if width ~= 0 then
                for h in node.traverse_id('hlist',list) do
                    if h.width ~= width then
                        h.list = hpack(h.list,width,'exactly')
                        h.width = width
                    end
                end
            end
            box_n.width = width
        end
        -- we can also do something with lastlinelength
    end
end
\stoptyping

In the first loop we inspect all lines (nodes with type \type
{hlist}) and repack them to their natural width with \type
{node.hpack}. In the process we keep track of the maximum natural
width. In the second loop we repack the content again, but this
time permanently. Now we use the maximum encountered width which
is forced by the keyword \type {exactly}. Because all glue is
still present we automatically get the desired alignment. We
create local shortcuts to some node functions which makes it run
faster; keep in mind that this is a core function called many
times in a regular \CONTEXT\ job.

In \CONTEXT\ \MKIV\ you will find quite some \LUA\ code and often
it looks rather complex, especially if you have no clue why it's
needed. Think of \OPENTYPE\ font handling which involves locating
fonts, loading and caching them, storing features and later on
applying them to node lists, etc. However, once we are beyond the
stage of developing all the code that is needed to support the
basics, we will start doing the things that more relate to the
typesetting process itself, like the previous code. One of the
candidates for a similar \LUA\ based solution is for instance
column balancing. From the previous example code you can deduce
that manipulating the node lists from \LUA\ can make that easier.
Of course we're a few more years down the road then.

\stopcomponent