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