1if not modules then modules = { } end modules ['typo-drp'] = {
2 version = 1.001,
3 comment = "companion to typo-drp.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
10
11
12
13
14local tonumber, type, next = tonumber, type, next
15local ceil = math.ceil
16local settings_to_hash = utilities.parsers.settings_to_hash
17
18local trace_initials = false trackers.register("typesetters.initials", function(v) trace_initials = v end)
19local report_initials = logs.reporter("nodes","initials")
20
21local initials = typesetters.initials or { }
22typesetters.initials = initials or { }
23
24local nodes = nodes
25
26local tasks = nodes.tasks
27local enableaction = tasks.enableaction
28local disableaction = tasks.disableaction
29
30local nuts = nodes.nuts
31local tonut = nodes.tonut
32
33local getnext = nuts.getnext
34local getprev = nuts.getprev
35local getchar = nuts.getchar
36local getid = nuts.getid
37local getattr = nuts.getattr
38local getwhd = nuts.getwhd
39
40local getprop = nuts.getprop
41local setprop = nuts.setprop
42
43local setlink = nuts.setlink
44local setprev = nuts.setprev
45local setnext = nuts.setnext
46local setfont = nuts.setfont
47local setscale = nuts.setscale
48local setwhd = nuts.setwhd
49local setkern = nuts.setkern
50local setoffsets = nuts.setoffsets
51local setglyphdata = nuts.setglyphdata
52local getparstate = nuts.getparstate
53
54local hpack_nodes = nuts.hpack
55
56local nodecodes = nodes.nodecodes
57
58local nodepool = nuts.pool
59local new_kern = nodepool.kern
60
61local insertbefore = nuts.insertbefore
62local insertafter = nuts.insertafter
63local remove_node = nuts.remove
64
65local startofpar = nuts.startofpar
66
67local nextnode = nuts.traversers.node
68local nextglyph = nuts.traversers.glyph
69
70local setcoloring = nuts.colors.set
71
72local variables = interfaces.variables
73local v_default = variables.default
74local v_margin = variables.margin
75local v_auto = variables.auto
76local v_first = variables.first
77local v_keep = variables.keep
78local v_yes = variables.yes
79local v_last = variables.last
80
81local texget = tex.get
82local texset = tex.set
83local unsetvalue = attributes.unsetvalue
84
85local glyph_code = nodecodes.glyph
86local hlist_code = nodecodes.hlist
87local glue_code = nodecodes.glue
88local kern_code = nodecodes.kern
89local par_code = nodecodes.par
90
91local actions = { }
92local busyactions = { }
93initials.actions = actions
94initials.busyactions = actions
95
96local a_initial = attributes.private("initial")
97
98local category = characters.category
99
100local levels = { }
101local getlevel = tex.getnestlevel or function() return tex.getnest("prv") end
102
103local function set(par,specification)
104 enableaction("processors","typesetters.initials.handler")
105 if trace_initials then
106 report_initials("enabling initials")
107 end
108 levels[getlevel()] = true
109 setprop(par,a_initial,specification)
110end
111
112function initials.set(specification)
113 nuts.setparproperty(set,specification)
114end
115
116interfaces.implement {
117 name = "setinitial",
118 actions = initials.set,
119 arguments = {
120 {
121 { "location" },
122 { "enabled", "boolean" },
123 { "method" },
124 { "continue" },
125 { "distance" ,"dimen" },
126 { "hoffset" ,"dimen" },
127 { "voffset" ,"dimen" },
128 { "font", "integer" },
129 { "glyphscale", "integer" },
130 { "dynamic", "integer" },
131 { "ca", "integer" },
132 { "ma", "integer" },
133 { "ta", "integer" },
134 { "n", "integer" },
135 { "m", "integer" },
136 }
137 }
138}
139
140
141
142
143actions[v_default] = function(head,settings)
144 local skip = false
145 local busy = false
146
147 local first = getnext(head)
148 local indent = false
149
150 if first and getid(first) == hlist_code then
151 first = getnext(first)
152 indent = true
153 end
154
155 while first and getid(first) ~= glyph_code do
156 first = getnext(first)
157 end
158 if first and getid(first) == glyph_code then
159 local ma = settings.ma or 0
160 local ca = settings.ca
161 local ta = settings.ta
162 local last = first
163 local distance = settings.distance or 0
164 local voffset = settings.voffset or 0
165 local hoffset = settings.hoffset or 0
166 local parindent = texget("parindent")
167 local baseline = texget("baselineskip",false)
168 local lines = tonumber(settings.n) or 0
169 local dynamic = settings.dynamic
170 local font = settings.font
171 local scale = settings.glyphscale
172 local method = settings_to_hash(settings.method)
173 local length = tonumber(settings.m) or 1
174
175
176
177 if getattr(first,a_initial) then
178 for current in nextnode, getnext(first) do
179 if getattr(current,a_initial) then
180 last = current
181 else
182 break
183 end
184 end
185 elseif method[v_auto] then
186 local char = getchar(first)
187 local kind = category(char)
188 if kind == "po" or kind == "pi" then
189 if method[v_first] then
190
191 local next = getnext(first)
192 if not next then
193
194 return head
195 end
196 last = nil
197 for current in nextglyph, next do
198 head, first = remove_node(head,first,true)
199 first = current
200 last = first
201 break
202 end
203 if not last then
204
205 return head
206 end
207 else
208
209 local next = getnext(first)
210 if next and method[v_keep] then
211 skip = first
212 end
213 if not next then
214
215 return head
216 end
217 for current in nextglyph, next do
218 last = current
219 break
220 end
221 if last == first then
222 return head
223 end
224 end
225 elseif kind == "pf" then
226
227 else
228
229 end
230
231 local next = getnext(first)
232 if next then
233 for current, char in nextglyph, next do
234 local kind = category(char)
235 if kind == "po" then
236 if method[v_last] then
237
238 remove_node(head,current,true)
239 else
240
241 last = current
242 end
243 end
244 break
245 end
246 end
247 else
248 for current in nextglyph, first do
249 last = current
250 if length <= 1 then
251 break
252 else
253 length = length - 1
254 end
255 end
256 end
257 local current = first
258 while true do
259 local id = getid(current)
260 if id == kern_code then
261 setkern(current,0)
262 elseif id == glyph_code and skip ~= current then
263 local next = getnext(current)
264 if font then
265 setfont(current,font)
266 end
267 if scale then
268 setscale(current,scale)
269 end
270 if dynamic > 0 then
271 setglyphdata(current,dynamic)
272 end
273 setcoloring(current,ma,ca,ta)
274 end
275 if current == last then
276 break
277 else
278 current = getnext(current)
279 end
280 end
281
282
283
284 local prev = getprev(first)
285 local next = getnext(last)
286
287 setprev(first)
288 setnext(last)
289 local dropper = hpack_nodes(first)
290 local width, height, depth = getwhd(dropper)
291 setwhd(dropper,0,0,0)
292
293 setlink(prev,dropper,next)
294
295 if next then
296 local current = next
297 while current do
298 local id = getid(current)
299 if id == glue_code or id == kern_code then
300 local next = getnext(current)
301
302 remove_node(head,current,true)
303 current = next
304 else
305 break
306 end
307 end
308 end
309
310 local hoffset = width + hoffset + distance + (indent and parindent or 0)
311 for current in nextglyph, first do
312 if skip == current then
313 setoffsets(current,-hoffset,0)
314 else
315 setoffsets(current,-hoffset,-voffset)
316 end
317 if current == last then
318 break
319 end
320 end
321
322 first = dropper
323
324 if settings.location == v_margin then
325
326 else
327 if lines == 0 then
328 lines = ceil((height+voffset) / baseline)
329 end
330
331
332 local hangafter = - lines
333 local hangindent = width + distance
334 if trace_initials then
335 report_initials("setting hangafter to %i and hangindent to %p",hangafter,hangindent)
336 end
337 if settings.continue == v_yes then
338 local state = {
339 lines = lines,
340 hangindent = hangindent,
341 parindent = indent and parindent,
342 }
343 busy = function(head)
344 local lines = state.lines
345 local done = getparstate(head,true).prevgraf or lines
346 if done < lines then
347 texset("hangafter",-(lines - done),true)
348 texset("hangindent",state.hangindent,true)
349 if state.parindent then
350 insertafter(first,first,new_kern(-state.parindent))
351 end
352 state.lines = lines - done
353 return busy
354 else
355 return false
356 end
357 end
358 end
359 texset("hangafter",hangafter,true)
360 texset("hangindent",hangindent,true)
361
362
363
364
365
366
367
368
369
370 end
371 if indent then
372 insertafter(first,first,new_kern(-parindent))
373 end
374 end
375 return head, busy
376end
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400local busy = false
401
402function initials.handler(head,groupcode)
403 if getid(head) == par_code and startofpar(head) then
404 if busy then
405 busy = busy(head)
406 end
407 if not busy then
408 local settings = getprop(head,a_initial)
409 if not settings or settings.continue ~= v_yes then
410 levels[getlevel()] = nil
411 if not next(levels) then
412 disableaction("processors","typesetters.initials.handler")
413 if trace_initials then
414 report_initials("disabling initials")
415 end
416 end
417 end
418 if settings then
419 local alternative = settings.alternative or v_default
420 local action = actions[alternative] or actions[v_default]
421 if action then
422 if trace_initials then
423 report_initials("processing initials, alternative %a",alternative)
424 end
425 head, busy = action(head,settings)
426 return head
427 end
428 end
429 end
430 end
431 return head
432end
433
434 |