1if not modules then modules = { } end modules ['supp-box'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to supp-box.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10local report_hyphenation = logs.reporter("languages","hyphenation")
11
12local tonumber, next, type = tonumber, next, type
13
14local lpegmatch = lpeg.match
15
16local tex = tex
17local context = context
18local nodes = nodes
19
20local implement = interfaces.implement
21
22local ctx_doifelse = commands.doifelse
23
24local nodecodes = nodes.nodecodes
25
26local disc_code = nodecodes.disc
27local hlist_code = nodecodes.hlist
28local vlist_code = nodecodes.vlist
29local glue_code = nodecodes.glue
30local penalty_code = nodecodes.penalty
31local glyph_code = nodecodes.glyph
32local par_code = nodecodes.par
33
34local indentlist_code = nodes.listcodes.indent
35local topskip_code = nodes.gluecodes.topskip
36local indentskip_code = nodes.gluecodes.indentskip
37local line_code = nodes.listcodes.line
38
39local hmode_code = tex.modelevels.horizontal
40
41local nuts = nodes.nuts
42local tonut = nuts.tonut
43local tonode = nuts.tonode
44
45local getnext = nuts.getnext
46local getprev = nuts.getprev
47local getboth = nuts.getboth
48local getdisc = nuts.getdisc
49local getid = nuts.getid
50local getsubtype = nuts.getsubtype
51local getlist = nuts.getlist
52local getattribute = nuts.getattribute
53local getbox = nuts.getbox
54local getdirection = nuts.getdirection
55local getwidth = nuts.getwidth
56local getheight = nuts.getheight
57local getdepth = nuts.getdepth
58local getwhd = nuts.getwhd
59local takebox = nuts.takebox
60
61local setlink = nuts.setlink
62local setboth = nuts.setboth
63local setnext = nuts.setnext
64local setprev = nuts.setprev
65local setbox = nuts.setbox
66local setlist = nuts.setlist
67local setdisc = nuts.setdisc
68local setwidth = nuts.setwidth
69local setheight = nuts.setheight
70local setdepth = nuts.setdepth
71local setshift = nuts.setshift
72local setsplit = nuts.setsplit
73local setattrlist = nuts.setattrlist
74local setwhd = nuts.setwhd
75local setglue = nuts.setglue
76
77local flushnode = nuts.flushnode
78local flushlist = nuts.flushlist
79local copy_node = nuts.copy
80local copylist = nuts.copylist
81local find_tail = nuts.tail
82local getdimensions = nuts.dimensions
83local hpack = nuts.hpack
84local vpack = nuts.vpack
85local traverseid = nuts.traverseid
86
87local free = nuts.free
88local findtail = nuts.tail
89local reverse = nuts.reverse
90local effectiveglue = nuts.effectiveglue
91
92local nextdisc = nuts.traversers.disc
93local nextdir = nuts.traversers.dir
94local nexthlist = nuts.traversers.hlist
95
96local countnodes = nuts.count
97
98
99local listtoutf = nodes.listtoutf
100
101local nodepool = nuts.pool
102local new_penalty = nodepool.penalty
103local new_hlist = nodepool.hlist
104local new_glue = nodepool.glue
105
106local setlistcolor = nodes.tracers.colors.setlist
107
108local texget = tex.get
109local texgetbox = tex.getbox
110local texisdimen = tex.isdimen
111local texsetdimen = tex.setdimen
112local texgetnest = tex.getnest
113
114local values = tokens.values
115local dimension_value <const> = values.dimension
116local cardinal_value <const> = values.cardinal
117local direct_value <const> = values.direct
118local conditional_value <const> = values.conditional
119
120local d_lastnaturalboxwd = texisdimen("lastnaturalboxwd")
121local d_lastnaturalboxht = texisdimen("lastnaturalboxht")
122local d_lastnaturalboxdp = texisdimen("lastnaturalboxdp")
123
124local d_givenwidth = texisdimen("givenwidth")
125local d_givenheight = texisdimen("givenheight")
126local d_givendepth = texisdimen("givendepth")
127
128local function hyphenatedlist(head,usecolor)
129 local current = head and tonut(head)
130 while current do
131 local id = getid(current)
132 local prev, next = getboth(current)
133 if id == disc_code then
134 local pre, post, replace = getdisc(current)
135 if not usecolor then
136
137 elseif pre and post then
138 setlistcolor(pre,"darkmagenta")
139 setlistcolor(post,"darkcyan")
140 elseif pre then
141 setlistcolor(pre,"darkyellow")
142 elseif post then
143 setlistcolor(post,"darkyellow")
144 end
145 if replace then
146 flushlist(replace)
147 end
148 setdisc(current)
149 if pre then
150 setlink(prev,new_penalty(10000),pre)
151 setlink(find_tail(pre),current)
152 end
153 if post then
154 setlink(current,new_penalty(10000),post)
155 setlink(find_tail(post),next)
156 end
157 elseif id == vlist_code or id == hlist_code then
158 hyphenatedlist(getlist(current))
159 end
160 current = next
161 end
162end
163
164implement {
165 name = "hyphenatedlist",
166 arguments = { "integer", "boolean" },
167 actions = function(n,color)
168 local b = texgetbox(n)
169 if b then
170 hyphenatedlist(b.list,color)
171 end
172 end
173}
174
175local function checkedlist(list)
176 if type(list) == "number" then
177 return getlist(getbox(tonut(list)))
178 else
179 return tonut(list)
180 end
181end
182
183implement {
184 name = "showhyphenatedinlist",
185 arguments = "integer",
186 actions = function(n)
187
188 local l = languages.hyphenators.handler(checkedlist(n))
189 report_hyphenation("show: %s",listtoutf(l,false,true))
190 end
191}
192
193do
194
195 local function applytochars(current,doaction,noaction,nested)
196 while current do
197 local id = getid(current)
198 if nested and (id == hlist_code or id == vlist_code) then
199 context.beginhbox()
200 applytochars(getlist(current),doaction,noaction,nested)
201 context.endhbox()
202 elseif id ~= glyph_code then
203 noaction(tonode(copy_node(current)))
204 else
205 doaction(tonode(copy_node(current)))
206 end
207 current = getnext(current)
208 end
209 end
210
211 local function applytowords(current,doaction,noaction,nested)
212 local start
213 while current do
214 local id = getid(current)
215 if id == glue_code then
216 if start then
217 doaction(tonode(copylist(start,current)))
218 start = nil
219 end
220 noaction(tonode(copy_node(current)))
221 elseif nested and (id == hlist_code or id == vlist_code) then
222 context.beginhbox()
223 applytowords(getlist(current),doaction,noaction,nested)
224 context.egroup()
225 elseif not start then
226 start = current
227 end
228 current = getnext(current)
229 end
230 if start then
231 doaction(tonode(copylist(start)))
232 end
233 end
234
235 local methods = {
236 char = applytochars,
237 characters = applytochars,
238 word = applytowords,
239 words = applytowords,
240 }
241
242 implement {
243 name = "applytobox",
244 arguments = {
245 {
246 { "box", "integer" },
247 { "command" },
248 { "method" },
249 { "nested", "boolean" },
250 }
251 },
252 actions = function(specification)
253 local list = checkedlist(specification.box)
254 local action = methods[specification.method or "char"]
255 if list and action then
256 action(list,context[specification.command or "ruledhbox"],context,specification.nested)
257 end
258 end
259 }
260
261 local split_char = lpeg.Ct(lpeg.C(1)^0)
262 local split_word = lpeg.tsplitat(lpeg.patterns.space)
263 local split_line = lpeg.tsplitat(lpeg.patterns.eol)
264
265 local function processsplit(specification)
266 local str = specification.data or ""
267 local command = specification.command or "ruledhbox"
268 local method = specification.method or "word"
269 local spaced = specification.spaced
270 if command then
271 command = context[command]
272 end
273 if method == "char" or method == "character" then
274 if spaced then
275 spaced = context.space
276 end
277 local words = lpegmatch(split_char,str)
278 for i=1,#words do
279 local word = words[i]
280 if word == " " then
281 if spaced then
282 spaced()
283 end
284 elseif command then
285 command(word)
286 else
287 context(word)
288 end
289 end
290 elseif method == "word" then
291 if spaced then
292 spaced = context.space
293 end
294 local words = lpegmatch(split_word,str)
295 for i=1,#words do
296 local word = words[i]
297 if spaced and i > 1 then
298 spaced()
299 end
300 if command then
301 command(word)
302 else
303 context(word)
304 end
305 end
306 elseif method == "line" then
307 if spaced then
308 spaced = context.par
309 end
310 local words = lpegmatch(split_line,str)
311 for i=1,#words do
312 local word = words[i]
313 if spaced and i > 1 then
314 spaced()
315 end
316 if command then
317 command(word)
318 else
319 context(word)
320 end
321 end
322 else
323 context(str)
324 end
325 end
326
327 implement {
328 name = "processsplit",
329 actions = processsplit,
330 arguments = {
331 {
332 { "data" },
333 { "command" },
334 { "method" },
335 { "spaced", "boolean" },
336 }
337 }
338 }
339
340end
341
342do
343
344 local a_vboxtohboxseparator = attributes.private("vboxtohboxseparator")
345
346 implement {
347 name = "vboxlisttohbox",
348 arguments = { "integer", "integer", "dimen" },
349 actions = function(original,target,inbetween)
350 local current = getlist(getbox(original))
351 local head = nil
352 local tail = nil
353 while current do
354 local id = getid(current)
355 local next = getnext(current)
356 if id == hlist_code then
357 local list = getlist(current)
358 if head then
359 if inbetween > 0 then
360 local n = new_glue(0,0,inbetween)
361 setlink(tail,n)
362 tail = n
363 end
364 setlink(tail,list)
365 else
366 head = list
367 end
368 tail = find_tail(list)
369
370 if getid(tail) == hlist_code and getattribute(tail,a_vboxtohboxseparator) == 1 then
371 local temp = tail
372 local prev = getprev(tail)
373 if next then
374 local list = getlist(tail)
375 setlink(prev,list)
376 setlist(tail)
377 tail = find_tail(list)
378 else
379 tail = prev
380 end
381 flushnode(temp)
382 end
383
384 setnext(tail)
385 setlist(current)
386 end
387 current = next
388 end
389 local result = new_hlist()
390 setlist(result,head)
391 setbox(target,result)
392
393 end
394 }
395
396 implement {
397 name = "hboxtovbox",
398 arguments = "integer",
399 actions = function(n)
400 local b = getbox(n)
401 local factor = texget("baselineskip",false) / texget("hsize")
402 setdepth(b,0)
403 setheight(b,getwidth(b) * factor)
404 end
405 }
406
407end
408
409implement {
410 name = "boxtostring",
411 arguments = "integer",
412 public = true,
413 actions = function(n)
414 local b = texgetbox(n)
415 local l = b and b.list
416 if l then
417
418 local s = nodes.toutf(l)
419 if s then
420 context.verbatim(s)
421 end
422 end
423 end
424}
425
426do
427
428
429
430 local function getnaturaldimensions(n)
431 local w = 0
432 local h = 0
433 local d = 0
434 local l = getlist(getbox(n))
435 if l then
436 w, h, d = getdimensions(l)
437 end
438 texsetdimen(d_lastnaturalboxwd,w)
439 texsetdimen(d_lastnaturalboxht,h)
440 texsetdimen(d_lastnaturalboxdp,d)
441 return w, h, d
442 end
443
444 local function setboxtonaturalwd(n)
445 local old = takebox(n)
446 local new = hpack(getlist(old))
447 setlist(old,nil)
448 flushnode(old)
449 setbox(n,new)
450 end
451
452 implement {
453 name = "getnaturaldimensions",
454 arguments = "integer",
455 public = true,
456 protected = true,
457 untraced = true,
458 actions = getnaturaldimensions
459 }
460
461 implement {
462 name = "naturalwd",
463 arguments = "integer",
464 usage = "value",
465 public = true,
466 protected = true,
467 actions = function(n)
468 return dimension_value, (getnaturaldimensions(n))
469 end
470 }
471
472 implement {
473 name = "getnaturalwd",
474 arguments = "integer",
475 usage = "value",
476 public = true,
477 protected = true,
478 actions = function(n)
479 local l = getlist(getbox(n))
480 return dimension_value, l and getdimensions(l) or 0
481 end
482 }
483
484 implement {
485 name = "setnaturalwd",
486 arguments = "integer",
487 public = true,
488 protected = true,
489 untraced = true,
490 actions = setboxtonaturalwd
491 }
492
493 nodes.setboxtonaturalwd = setboxtonaturalwd
494
495end
496
497do
498
499 local directioncodes = tex.directioncodes
500 local lefttoright_code = directioncodes.lefttoright
501 local righttoleft_code = directioncodes.righttoleft
502
503 local function firstdirinbox(n)
504 local b = getbox(n)
505 if b then
506 local l = getlist(b)
507 if l then
508 for d in nextdir, l do
509 return getdirection(d)
510 end
511 for h in nexthlist, l do
512 return getdirection(h)
513 end
514 end
515 end
516 return lefttoright_code
517 end
518
519 nodes.firstdirinbox = firstdirinbox
520
521 implement {
522 name = "doifelserighttoleftinbox",
523 arguments = "integer",
524 actions = function(n)
525 ctx_doifelse(firstdirinbox(n) == righttoleft_code)
526 end
527 }
528
529end
530
531
532
533do
534
535 local tonut = nodes.tonut
536 local takebox = nuts.takebox
537 local flushlist = nuts.flushlist
538 local copylist = nuts.copylist
539 local getwhd = nuts.getwhd
540 local setbox = nuts.setbox
541 local new_hlist = nuts.pool.hlist
542
543 local boxes = { }
544 nodes.boxes = boxes
545 local cache = table.setmetatableindex("table")
546 local report = logs.reporter("boxes","cache")
547 local trace = false
548
549 trackers.register("nodes.boxes",function(v) trace = v end)
550
551 function boxes.save(category,name,b)
552 name = tonumber(name) or name
553 local b = takebox(b)
554 if trace then
555 report("category %a, name %a, %s (%s)",category,name,"save",b and "content" or "empty")
556 end
557 if name == "+" then
558 name = #cache[category] + 1
559 end
560 cache[category][name] = b or false
561 end
562
563 function boxes.savenode(category,name,n)
564 name = tonumber(name) or name
565 if trace then
566 report("category %a, name %a, %s (%s)",category,name,"save",n and "content" or "empty")
567 end
568 cache[category][name] = tonut(n) or false
569 end
570
571 function boxes.found(category,name)
572 name = tonumber(name) or name
573 return cache[category][name] and true or false
574 end
575
576 function boxes.count(category)
577 return #cache[category]
578 end
579
580 function boxes.direct(category,name,copy)
581 name = tonumber(name) or name
582 local c = cache[category]
583 local b = c[name]
584
585
586
587
588
589 if not b then
590
591 elseif copy then
592 b = copylist(b)
593 else
594 c[name] = false
595 end
596 if trace then
597 report("category %a, name %a, %s (%s)",category,name,"direct",b and "content" or "empty")
598 end
599 if b then
600 return tonode(b)
601 end
602 end
603
604 function boxes.restore(category,name,box,copy)
605 name = tonumber(name) or name
606 local c = cache[category]
607 local b = takebox(box)
608 if b then
609 flushlist(b)
610 end
611 local b = c[name]
612 if not b then
613
614 elseif copy then
615 b = copylist(b)
616 else
617 c[name] = false
618 end
619 if trace then
620 report("category %a, name %a, %s (%s)",category,name,"restore",b and "content" or "empty")
621 end
622 setbox(box,b or nil)
623 end
624
625 function boxes.prune(category)
626
627 local c = cache[category]
628 local t = { }
629 local n = 0
630 for i=1,#c do
631 local ci = c[i]
632 if ci then
633 n = n + 1 ; t[n] = ci
634 end
635 end
636 cache[category] = t
637 end
638
639 local function dimensions(category,name)
640 name = tonumber(name) or name
641 local b = cache[category][name]
642 if b then
643 return getwhd(b)
644 else
645 return 0, 0, 0
646 end
647 end
648
649 boxes.dimensions = dimensions
650
651 function boxes.reset(category,name)
652 name = tonumber(name) or name
653 local c = cache[category]
654 if name and name ~= "" then
655 local b = c[name]
656 if b then
657 flushlist(b)
658 c[name] = false
659 end
660 if trace then
661 report("category %a, name %a, reset",category,name)
662 end
663 else
664 for k, b in next, c do
665 if b then
666 flushlist(b)
667 end
668 end
669 cache[category] = { }
670 if trace then
671 report("category %a, reset",category)
672 end
673 end
674 end
675
676 function boxes.dispose(category)
677 boxes.reset(category)
678 cache[category] = nil
679 end
680
681 implement {
682 name = "putboxincache",
683 public = true,
684 protected = true,
685 arguments = { "string", "string", "integer" },
686 actions = boxes.save,
687 }
688
689 implement {
690 name = "getboxfromcache",
691 public = true,
692 protected = true,
693 arguments = { "string", "string", "integer" },
694 actions = boxes.restore,
695 }
696
697 implement {
698 name = "directboxfromcache",
699 public = true,
700 protected = true,
701 arguments = "2 strings",
702 actions = { boxes.direct, context },
703
704 }
705
706 implement {
707 name = "directcopyboxfromcache",
708 public = true,
709 protected = true,
710 arguments = { "string", "string", true },
711 actions = { boxes.direct, context },
712
713 }
714
715 implement {
716 name = "copyboxfromcache",
717 public = true,
718 protected = true,
719 arguments = { "string", "string", "integer", true },
720 actions = boxes.restore,
721 }
722
723 implement {
724 name = "doifelseboxincache",
725 public = true,
726 protected = true,
727 arguments = "2 strings",
728 actions = { boxes.found, ctx_doifelse },
729 }
730
731 implement {
732 name = "resetboxesincache",
733 public = true,
734 protected = true,
735 arguments = "string",
736 actions = boxes.reset,
737 }
738
739 implement {
740 name = "pruneboxesincache",
741 public = true,
742 protected = true,
743 arguments = "string",
744 actions = boxes.prune,
745 }
746
747 implement {
748 name = "disposeboxesincache",
749 public = true,
750 protected = true,
751 arguments = "string",
752 actions = boxes.dispose,
753 }
754
755
756
757 implement {
758 name = "getboxcountfromcache",
759 public = true,
760 protected = true,
761 usage = "value",
762 arguments = "string",
763 actions = function(category)
764 return cardinal_value, #cache[category]
765 end,
766 }
767
768 implement {
769 name = "getboxwdfromcache",
770 public = true,
771 protected = true,
772 usage = "value",
773 arguments = "2 strings",
774 actions = function(category,name)
775 local w, h, d = dimensions(category,name)
776 return dimension_value, w
777 end,
778 }
779
780 implement {
781 name = "getboxhtfromcache",
782 arguments = "2 strings",
783 public = true,
784 protected = true,
785 usage = "value",
786 actions = function(category,name)
787 local w, h, d = dimensions(category,name)
788 return dimension_value, h
789 end,
790 }
791
792 implement {
793 name = "getboxdpfromcache",
794 arguments = "2 strings",
795 public = true,
796 protected = true,
797 usage = "value",
798 actions = function(category,name)
799 local w, h, d = dimensions(category,name)
800 return dimension_value, d
801 end,
802 }
803
804 implement {
805 name = "getboxhtdpfromcache",
806 arguments = "2 strings",
807 public = true,
808 protected = true,
809 usage = "value",
810 actions = function(category,name)
811 local w, h, d = dimensions(category,name)
812 return dimension_value, h + d
813 end,
814 }
815
816 implement {
817 name = "putnextboxincache",
818 public = true,
819 protected = true,
820 arguments = { "string", "string", "box" },
821 actions = function(category,name,b)
822 name = tonumber(name) or name
823 b = tonut(b)
824 if trace then
825 report("category %a, name %a, %s (%s)",category,name,"save",b and "content" or "empty")
826 end
827 cache[category][name] = b or false
828 end
829 }
830
831end
832
833implement {
834 name = "lastlinewidth",
835 actions = function()
836 local head = tex.lists.pagehead
837
838 context(head and getdimensions(getlist(find_tail(tonut(tex.lists.pagehead)))) or 0)
839 end
840}
841
842implement {
843 name = "shiftbox",
844 arguments = { "integer", "dimension" },
845 actions = function(n,d)
846 setshift(getbox(n),d)
847 end,
848}
849
850implement { name = "vpackbox", arguments = "integer", actions = function(n) setbox(n,(vpack(takebox(n)))) end }
851implement { name = "hpackbox", arguments = "integer", actions = function(n) setbox(n,(hpack(takebox(n)))) end }
852
853implement { name = "vpackedbox", arguments = "integer", actions = function(n) context(vpack(takebox(n))) end }
854implement { name = "hpackedbox", arguments = "integer", actions = function(n) context(hpack(takebox(n))) end }
855
856implement {
857 name = "scangivendimensions",
858 public = true,
859 protected = true,
860 arguments = {
861 {
862 { "width", "dimension" },
863 { "height", "dimension" },
864 { "depth", "dimension" },
865 },
866 },
867 actions = function(t)
868 texsetdimen(d_givenwidth, t.width or 0)
869 texsetdimen(d_givenheight,t.height or 0)
870 texsetdimen(d_givendepth, t.depth or 0)
871 end,
872}
873
874local function stripglue(list)
875 local done = false
876 local first = list
877 while first do
878 local id = getid(first)
879 if id == glue_code or id == penalty_code then
880 first = getnext(first)
881 else
882 break
883 end
884 end
885 if first and first ~= list then
886
887 setsplit(getprev(first),first)
888 flushlist(list)
889 list = first
890 done = true
891 end
892 if list then
893 local tail = findtail(list)
894 local last = tail
895 while last do
896 local id = getid(last)
897 if id == glue_code or id == penalty_code then
898 last = getprev(last)
899 else
900 break
901 end
902 end
903 if last ~= tail then
904
905 flushlist(getnext(last))
906 setnext(last)
907 done = true
908 end
909 end
910 return list, done
911end
912
913local function limitate(t)
914 local text = t.text
915 if text then
916 text = tonut(text)
917 else
918 return
919 end
920 local sentinel = t.sentinel
921 if sentinel then
922 sentinel = tonut(sentinel)
923 local s = getlist(sentinel)
924 setlist(sentinel)
925 free(sentinel)
926 sentinel = s
927 else
928 return tonode(text)
929 end
930 local width = getwidth(text)
931 local list = getlist(text)
932 local done = false
933 if t.strip then
934 list, done = stripglue(list)
935 if not list then
936 setlist(text)
937 setwidth(text,0)
938 return text
939 elseif done then
940 width = getdimensions(list)
941 setlist(text,list)
942 end
943 end
944 local left = t.left or 0
945 local right = t.right or 0
946 local total = left + right
947 if total < width then
948 local last = nil
949 local first = nil
950 local maxleft = left
951 local maxright = right
952 local swidth = getwidth(sentinel)
953 if maxright > 0 then
954 maxleft = maxleft - swidth/2
955 maxright = maxright - swidth/2
956 else
957 maxleft = maxleft - swidth
958 end
959 for n in traverseid(glue_code,list) do
960 local width = getdimensions(list,n)
961 if width > maxleft then
962 if not last then
963 last = n
964 end
965 break
966 else
967 last = n
968 end
969 end
970 if last and maxright > 0 then
971 for n in traverseid(glue_code,last) do
972 local width = getdimensions(n)
973 if width < maxright then
974 first = n
975 break
976 else
977 first = n
978 end
979 end
980 end
981 if last then
982 local rest = getnext(last)
983 if rest then
984 local tail = findtail(sentinel)
985 if first and getid(first) == glue_code and getid(tail) == glue_code then
986 setwidth(first,0)
987 end
988 if last and getid(last) == glue_code and getid(sentinel) == glue_code then
989 setwidth(last,0)
990 end
991 if first and first ~= last then
992 local prev = getprev(first)
993 if prev then
994 setnext(prev)
995 end
996 setlink(tail,first)
997 end
998 setlink(last,sentinel)
999 setprev(rest)
1000 flushlist(rest)
1001 end
1002 end
1003 end
1004 setlist(text)
1005 free(text)
1006
1007 if t.freeze then
1008 local l = hpack(list,total,"exactly")
1009 for n in traverseid(glue_code,list) do
1010 setglue(n,(effectiveglue(n,l)))
1011 end
1012 setlist(l)
1013 flushnode(l)
1014 end
1015
1016 return tonode(list)
1017end
1018
1019implement {
1020 name = "limitated",
1021 public = true,
1022 protected = true,
1023 arguments = {
1024 {
1025 { "left", "dimension" },
1026 { "right", "dimension" },
1027 { "text", "hbox" },
1028 { "sentinel", "hbox" },
1029 { "strip", "boolean" },
1030 { "freeze", "boolean" },
1031 }
1032 },
1033 actions = function(t)
1034 context.dontleavehmode()
1035 context(limitate(t))
1036 end,
1037}
1038
1039
1040
1041do
1042
1043 local naturalhsize = nuts.naturalhsize
1044
1045 implement {
1046 name = "widthuptohere",
1047 public = true,
1048 usage = "value",
1049 actions = function()
1050 local n = texgetnest()
1051 return dimension_value, n.mode == hmode_code and naturalhsize(getnext(tonut(n.head))) or 0
1052 end,
1053 }
1054
1055end
1056
1057implement {
1058 name = "doifelseindented",
1059 public = true,
1060 protected = true,
1061 actions = function()
1062 local n = texgetnest()
1063 local b = false
1064
1065 if n.mode == hmode_code then
1066 n = tonut(n.head)
1067 while n do
1068 n = getnext(n)
1069 if n then
1070 local id = getid(n)
1071 if id == glue_code then
1072 if getsubtype(n) == indentskip_code then
1073 b = getwidth(n) > 0
1074 break
1075 end
1076 elseif id == hlist_code then
1077 if getsubtype(n) == indentlist_code then
1078 b = getwidth(n) > 0
1079 end
1080 break
1081 elseif id == par_code then
1082
1083 else
1084 break
1085 end
1086 end
1087 end
1088 end
1089 ctx_doifelse(b)
1090 end,
1091}
1092
1093implement {
1094 name = "noflinesinbox",
1095 public = true,
1096 protected = false,
1097 arguments = "integer",
1098 actions = function(n)
1099 local c = 0
1100 local b = getbox(n)
1101 if b then
1102 b = getlist(b)
1103 if b then
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114 c = countnodes(hlist_code,b) + countnodes(vlist_code,b)
1115 end
1116 end
1117 context(c)
1118
1119 end,
1120}
1121
1122do
1123
1124 local takebox = tex.takebox
1125
1126 implement {
1127 name = "thebox",
1128 public = true,
1129 arguments = "integer",
1130 actions = function(n)
1131 context(takebox(n))
1132 end
1133 }
1134
1135end
1136
1137
1138
1139implement {
1140 name = "reversevboxcontent",
1141 protected = true,
1142 public = true,
1143 arguments = "integer",
1144 actions = function(n)
1145 local b = getbox(n)
1146 local l = b and getid(b) == vlist_code and getlist(b)
1147 if l and getnext(l) then
1148 setlist(b,reverse(l))
1149 end
1150 end
1151}
1152
1153
1154
1155do
1156
1157 local scaninteger = tokens.scanners.integer
1158 local scanbox = tokens.scanners.box
1159 local scandimen = tokens.scanners.dimen
1160
1161 local setsubtype = nuts.setsubtype
1162 local removenode = nuts.remove
1163 local getnormalizedline = nuts.getnormalizedline
1164 local getdimensions = nuts.dimensions
1165 local getrangedimensions = nuts.rangedimensions
1166
1167 local setprop = nuts.setprop
1168 local getprop = nuts.getprop
1169
1170 local listcodes = nodes.listcodes
1171 local line_code = listcodes.line
1172 local equation_code = listcodes.equation
1173 local unknown_code = listcodes.unknown
1174
1175
1176
1177 local reporterror = logs.texerrormessage
1178
1179
1180
1181
1182
1183
1184 local function countlines(box)
1185 local prop = getprop(box,"boxlines")
1186 if not prop then
1187 local line = 0
1188 local list = getlist(box)
1189 prop = { }
1190 if list then
1191 for n, subtype in nexthlist, list do
1192 if subtype == line_code then
1193 line = line + 1
1194 prop[line] = n
1195 end
1196 end
1197 end
1198 setprop(box,"boxlines",prop)
1199 end
1200 return prop
1201 end
1202
1203 local function boxlinecount(what)
1204 local n = scaninteger()
1205 local box = getbox(n)
1206 if what == "value" then
1207 return cardinal_value, box and #countlines(box) or 0
1208 end
1209 end
1210
1211 local function findline()
1212 local n = scaninteger()
1213 local line = scaninteger()
1214 local box = getbox(n)
1215 if box then
1216 local prop = getprop(box,"boxlines")
1217 if not prop then
1218 prop = countlines(box)
1219 end
1220 local found = prop[line]
1221 if found then
1222 local props = getprop(found,"lineproperties")
1223 if not props then
1224 props = getnormalizedline(found)
1225 props.width, props.height, props.depth = getwhd(found)
1226 setprop(found,"lineproperties",props)
1227 end
1228 return props, line, found, box
1229 end
1230 end
1231 reporterror("no line %i in box %i",line,n)
1232 end
1233
1234 local function findrange()
1235 local n = scaninteger()
1236 local first = scaninteger()
1237 local last = scaninteger()
1238 local box = getbox(n)
1239 if box then
1240 local prop = getprop(box,"boxlines")
1241 if not prop then
1242 prop = countlines(box)
1243 end
1244 if first > 0 and last <= #prop then
1245 for i = first, last do
1246 local found = prop[i]
1247 local props = getprop(found,"lineproperties")
1248 if not props then
1249 props = getnormalizedline(found)
1250 props.width, props.height, props.depth = getwhd(found)
1251 setprop(found,"lineproperties",props)
1252 end
1253 end
1254 return prop, first, last, box
1255 end
1256 end
1257 reporterror("no lines %i - %i in box %i",first,last,n)
1258 end
1259
1260 local function getline(props,line,found,box,value)
1261 local p, n = getboth(found)
1262 local temp = new_hlist()
1263 setsubtype(temp,getsubtype(found))
1264 setwhd(temp,getwhd(found))
1265 if found == getlist(box) then
1266 setlink(temp,n)
1267 setlist(box,temp)
1268 else
1269 setlink(p,temp,n)
1270 end
1271 getprop(box,"boxlines")[line] = temp
1272 setboth(found)
1273 setsubtype(found, unknown_code)
1274 if value then
1275 return direct_value, found
1276 else
1277 context(tonode(found))
1278 end
1279 end
1280
1281 local function copyline(props,line,found,box,value)
1282 found = copy_node(found)
1283 setsubtype(found, unknown_code)
1284 if value then
1285 return direct_value, found
1286 else
1287 context(tonode(found))
1288 end
1289 end
1290
1291 local function setline(props,line,found,box)
1292 local p, n = getboth(found)
1293 local temp = scanbox()
1294 if temp then
1295 temp = tonut(temp)
1296 if found == getlist(box) then
1297 setlink(temp,n)
1298 setlist(box,temp)
1299 else
1300 setlink(p,temp,n)
1301 end
1302 flushnode(found)
1303 getprop(box,"boxlines")[line] = temp
1304 end
1305 end
1306
1307 local function naturaldimensions(p,l,found)
1308 if not p.naturalwidth then
1309 p.naturalwidth, p.naturalheight, p.naturaldepth = getdimensions(getlist(found))
1310 end
1311 return p
1312 end
1313
1314 local function rangedimensions(p,f,l,box)
1315 local w, h, d = getrangedimensions(box,p[f],getnext(p[l]),true)
1316 return { width = w, height = h, depth = d }
1317 end
1318
1319 local getters_one = {
1320
1321 ["wd"] = function(p,l,found) return dimension_value, p.width end,
1322 ["ht"] = function(p,l,found) return dimension_value, p.height end,
1323 ["dp"] = function(p,l,found) return dimension_value, p.depth end,
1324 ["ls"] = function(p,l,found) return dimension_value, p.leftskip end,
1325 ["rs"] = function(p,l,found) return dimension_value, p.rightskip end,
1326 ["lh"] = function(p,l,found) return dimension_value, p.lefthangskip end,
1327 ["rh"] = function(p,l,found) return dimension_value, p.righthangskip end,
1328 ["lp"] = function(p,l,found) return dimension_value, p.parfillleftskip end,
1329 ["rp"] = function(p,l,found) return dimension_value, p.parfillrightskip end,
1330 ["il"] = function(p,l,found) return dimension_value, p.parinitleftskip end,
1331 ["ir"] = function(p,l,found) return dimension_value, p.parinitrightskip end,
1332 ["in"] = function(p,l,found) return dimension_value, p.indent end,
1333
1334 ["nw"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturalwidth end,
1335 ["nh"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturalheigth end,
1336 ["nd"] = function(p,l,found) return dimension_value, naturaldimensions(p,l,found).naturaldepth end,
1337
1338 ["get"] = function(p,l,found,box) return getline(p,l,found,box,true) end,
1339 }
1340
1341 local getters_two = {
1342 ["wd"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).width end,
1343 ["ht"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).height end,
1344 ["dp"] = function(p,f,l,box) return dimension_value, rangedimensions(p,f,l,box).depth end,
1345 }
1346
1347 local setters_one = {
1348 ["wd"] = function(p,l,found) return setwidth (found,scandimen(false,false,true)) end,
1349 ["ht"] = function(p,l,found) return setheight(found,scandimen(false,false,true)) end,
1350 ["dp"] = function(p,l,found) return setdepth (found,scandimen(false,false,true)) end,
1351 ["set"] = setline,
1352 ["get"] = getline,
1353 ["copy"] = copyline,
1354 }
1355
1356 local function boxline(name,what)
1357 local props, line, found, box = findline()
1358 if not found then
1359
1360 elseif what == "value" then
1361 local getter = getters_one[name]
1362 if getter then
1363 return getter(props,line,found,box)
1364 end
1365 else
1366 local setter = setters_one[name]
1367 if setter then
1368 return setter(props,line,found,box)
1369 end
1370 end
1371 end
1372
1373
1374
1375 local function boxrange(name,what)
1376 local prop, first, last, box = findrange()
1377 if not prop then
1378
1379 elseif what == "value" then
1380 local getter = getters_two[name]
1381 if getter then
1382 return getter(prop,first,last,box)
1383 end
1384 else
1385 local setter = setters_two[name]
1386 if setter then
1387 return setter(prop,first,last,box)
1388 end
1389 end
1390 end
1391
1392 local function define_one(name,action)
1393 implement {
1394 name = name,
1395 public = true,
1396 usage = "value",
1397 actions = function(what) return boxline(action,what) end,
1398 }
1399 end
1400
1401 local function define_two(name,action)
1402 implement {
1403 name = name,
1404 public = true,
1405 usage = "value",
1406 actions = function(what) return boxrange(action,what) end,
1407 }
1408 end
1409
1410 implement {
1411 name = "boxlines",
1412 public = true,
1413 usage = "value",
1414 actions = boxlinecount,
1415 }
1416
1417
1418
1419 define_one("boxline", "get")
1420 define_one("setboxline", "set")
1421 define_one("copyboxline", "copy")
1422 define_one("boxlineht", "ht")
1423 define_one("boxlinedp", "dp")
1424 define_one("boxlinewd", "wd")
1425 define_one("boxlinels", "ls")
1426 define_one("boxliners", "rs")
1427 define_one("boxlinelh", "lh")
1428 define_one("boxlinerh", "rh")
1429 define_one("boxlinelp", "lp")
1430 define_one("boxlinerp", "rp")
1431 define_one("boxlineil", "il")
1432 define_one("boxlineir", "ir")
1433 define_one("boxlinein", "in")
1434 define_one("boxlinenw", "nw")
1435 define_one("boxlinenh", "nh")
1436 define_one("boxlinend", "nd")
1437
1438 define_two("boxrangewd", "wd")
1439 define_two("boxrangeht", "ht")
1440 define_two("boxrangedp", "dp")
1441
1442end
1443
1444do
1445
1446 local getbox = tex.getbox
1447 local setfield = nodes.setfield
1448 local getfield = nodes.getfield
1449 local flush = nodes.flushnode
1450 local copynode = nodes.copy
1451
1452 local function get(n,field,copy)
1453 local b = getbox(n)
1454 if b then
1455 local p = getfield(b,field)
1456 if copy then
1457 p = copynode(p)
1458 else
1459 setfield(b,field)
1460 end
1461 context(p)
1462 end
1463 end
1464
1465 local function set(n,l,field)
1466 local b = getbox(n)
1467 if b then
1468 setfield(b,field,l)
1469 else
1470 flush(l)
1471 end
1472 end
1473
1474 implement {
1475 name = "prelistbox",
1476 public = true,
1477 usage = "value",
1478 arguments = { "integer", '"pre"' },
1479 actions = get,
1480 }
1481
1482 implement {
1483 name = "postlistbox",
1484 public = true,
1485 usage = "value",
1486 arguments = { "integer", '"post"' },
1487 actions = get,
1488 }
1489
1490 implement {
1491 name = "prelistcopy",
1492 public = true,
1493 usage = "value",
1494 arguments = { "integer", '"pre"', true },
1495 actions = get,
1496 }
1497
1498 implement {
1499 name = "postlistcopy",
1500 public = true,
1501 usage = "value",
1502 arguments = { "integer", '"post"', true },
1503 actions = get,
1504 }
1505
1506 implement {
1507 name = "setprelistbox",
1508 public = true,
1509 usage = "value",
1510 arguments = { "integer", "box", '"pre"' },
1511 actions = set,
1512 }
1513
1514 implement {
1515 name = "setpostlistbox",
1516 public = true,
1517 usage = "value",
1518 arguments = { "integer", "box", '"post"' },
1519 actions = set,
1520 }
1521
1522end
1523
1524do
1525
1526 local function setsplitlisthtdp(n,ht,dp)
1527 local box = getbox(n)
1528 if box then
1529 local head = getlist(box)
1530 if head then
1531 local tail = findtail(head)
1532 local id = getid(head)
1533 if id == glue_code and getsubtype(head) == topskip_code then
1534 head = getnext(head)
1535 id = head and getid(head)
1536 ht = ht - getwidth(head)
1537 end
1538 if id == hlist_code and getsubtype(head) == line_code and getheight(head) < ht then
1539
1540 setheight(head,ht)
1541 end
1542 local id = getid(tail)
1543 if id == glue_code then
1544 tail = getprev(tail)
1545 id = tail and getid(tail)
1546 dp = dp - getwidth(tail)
1547 end
1548 if id == hlist_code and getsubtype(tail) == line_code and getdepth(tail) < dp then
1549 setdepth(dept,dp)
1550
1551 end
1552 end
1553 end
1554 end
1555
1556 implement {
1557 name = "setsplitlisthtdp",
1558 public = true,
1559 protected = true,
1560 arguments = { "integer", "dimen", "dimen" },
1561 actions = setsplitlisthtdp,
1562 }
1563end
1564
1565do
1566
1567 local ctx_validtext = context.core.validtext
1568
1569 implement {
1570 name = "iftext",
1571 public = true,
1572 usage = "condition",
1573 actions = function()
1574 ctx_validtext()
1575 return conditional_value
1576 end
1577 }
1578
1579end
1580 |