1if not modules then modules = { } end modules ['typo-pag'] = {
2 version = 1.001,
3 comment = "companion to typo-pag.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10builders = builders or { }
11local builders = builders
12
13builders.paragraphs = builders.paragraphs or { }
14local parbuilders = builders.paragraphs
15
16local nodes = nodes
17local nodecodes = nodes.nodecodes
18
19local hlist_code = nodecodes.hlist
20local vlist_code = nodecodes.vlist
21local glue_code = nodecodes.glue
22local kern_code = nodecodes.kern
23local penalty_code = nodecodes.penalty
24
25local unsetvalue = attributes.unsetvalue
26local a_keeptogether = attributes.private("keeptogether")
27
28local nuts = nodes.nuts
29
30local getnext = nuts.getnext
31local getprev = nuts.getprev
32local getid = nuts.getid
33local getattr = nuts.getattr
34local takeattr = nuts.takeattr
35local setattr = nuts.setattr
36local getwhd = nuts.getwhd
37local getkern = nuts.getkern
38local setpenalty = nuts.setpenalty
39local getwidth = nuts.getwidth
40local getdepth = nuts.getdepth
41
42local insertnodeafter = nuts.insertafter
43local new_penalty = nuts.pool.penalty
44
45local trace_keeptogether = false
46local report_keeptogether = logs.reporter("parbuilders","keeptogether")
47
48local enableaction = nodes.tasks.enableaction
49
50local cache = { }
51local last = 0
52local enabled = false
53
54trackers.register("parbuilders.keeptogether", function(v) trace_keeptogether = v end)
55
56
57
58
59function parbuilders.registertogether(line,specification)
60 if not specification then
61 return
62 end
63 if not enabled then
64 enableaction("finalizers","builders.paragraphs.keeptogether")
65 end
66 local a = getattr(line,a_keeptogether)
67 local c = a and cache[a]
68 if c then
69 local height = specification.height
70 local depth = specification.depth
71 local slack = specification.slack
72 if height and height > c.height then
73 c.height = height
74 end
75 if depth and depth > c.depth then
76 c.depth = depth
77 end
78 if slack and slack > c.slack then
79 c.slack = slack
80 end
81 else
82 last = last + 1
83 cache[last] = specification
84 if not specification.height then
85 specification.height = 0
86 end
87 if not specification.depth then
88 specification.depth = 0
89 end
90 if not specification.slack then
91 specification.slack = 0
92 end
93 setattr(line,a_keeptogether,last)
94 end
95 if trace_keeptogether then
96 local a = a or last
97 local c = cache[a]
98 local noflines = specification.lineheight
99 local height = c.height
100 local depth = c.depth
101 local slack = c.slack
102 if not noflines or noflines == 0 then
103 noflines = "unknown"
104 else
105 noflines = math.round((height + depth - slack) / noflines)
106 end
107 report_keeptogether("registered, index %s, height %p, depth %p, slack %p, noflines %a",a,height,depth,slack,noflines)
108 end
109end
110
111local function keeptogether(start,a,specification)
112 local current = getnext(start)
113 local previous = start
114 local total = getdepth(previous)
115 local slack = specification.slack
116 local threshold = specification.depth - slack
117 if trace_keeptogether then
118 report_keeptogether("%s, index %s, total %p, threshold %p, slack %p","list",a,total,threshold,slack)
119 end
120 while current do
121 local id = getid(current)
122 if id == vlist_code or id == hlist_code then
123 local wd, ht, dp = getwhd(current)
124 total = total + ht + dp
125 if trace_keeptogether then
126 report_keeptogether("%s, index %s, total %p, threshold %p","list",a,total,threshold)
127 end
128 if total <= threshold then
129 if getid(previous) == penalty_code then
130 setpenalty(previous,10000)
131 else
132 insertnodeafter(head,previous,new_penalty(10000))
133 end
134 else
135 break
136 end
137 elseif id == glue_code then
138
139 total = total + getwidth(current)
140 if trace_keeptogether then
141 report_keeptogether("%s, index %s, total %p, threshold %p","glue",a,total,threshold)
142 end
143 if total <= threshold then
144 if getid(previous) == penalty_code then
145 setpenalty(previous,10000)
146 else
147 insertnodeafter(head,previous,new_penalty(10000))
148 end
149 else
150 break
151 end
152 elseif id == kern_code then
153 total = total + getkern(current)
154 if trace_keeptogether then
155 report_keeptogether("%s, index %s, total %s, threshold %s","kern",a,total,threshold)
156 end
157 if total <= threshold then
158 if getid(previous) == penalty_code then
159 setpenalty(previous,10000)
160 else
161 insertnodeafter(head,previous,new_penalty(10000))
162 end
163 else
164 break
165 end
166 elseif id == penalty_code then
167 if total <= threshold then
168 if getid(previous) == penalty_code then
169 setpenalty(previous,10000)
170 end
171 setpenalty(current,10000)
172 else
173 break
174 end
175 end
176 previous = current
177 current = getnext(current)
178 end
179end
180
181
182
183function parbuilders.keeptogether(head)
184 local done = false
185 local current = head
186 while current do
187 if getid(current) == hlist_code then
188 local a = takeattr(current,a_keeptogether)
189 if a and a > 0 then
190 local specification = cache[a]
191 if specification then
192 keeptogether(current,a,specification)
193
194
195
196 cache[a] = nil
197 done = true
198 end
199 end
200 end
201 current = getnext(current)
202 end
203 return head, done
204end
205 |