1if not modules then modules = { } end modules ['typo-duc'] = {
2 version = 1.001,
3 comment = "companion to typo-dir.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files",
7 comment = "Unicode bidi (sort of) variant c",
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
49local insert, remove, unpack, concat = table.insert, table.remove, table.unpack, table.concat
50local utfchar = utf.char
51local setmetatable = setmetatable
52local formatters = string.formatters
53
54local directiondata = characters.directions
55local mirrordata = characters.mirrors
56local textclassdata = characters.textclasses
57
58local nuts = nodes.nuts
59
60local getnext = nuts.getnext
61local getprev = nuts.getprev
62local getid = nuts.getid
63local getsubtype = nuts.getsubtype
64local getlist = nuts.getlist
65local getchar = nuts.getchar
66local getattr = nuts.getattr
67local getprop = nuts.getprop
68local getdirection = nuts.getdirection
69local isglyph = nuts.isglyph
70
71local setprop = nuts.setprop
72local setchar = nuts.setchar
73local setdirection = nuts.setdirection
74local setattrlist = nuts.setattrlist
75
76local properties = nodes.properties.data
77
78local remove_node = nuts.remove
79local insertnodeafter = nuts.insertafter
80local insertnodebefore = nuts.insertbefore
81
82local startofpar = nuts.startofpar
83
84local nodepool = nuts.pool
85local new_direction = nodepool.direction
86
87local nodecodes = nodes.nodecodes
88local gluecodes = nodes.gluecodes
89
90local glyph_code = nodecodes.glyph
91local glue_code = nodecodes.glue
92local hlist_code = nodecodes.hlist
93local vlist_code = nodecodes.vlist
94local math_code = nodecodes.math
95local dir_code = nodecodes.dir
96local par_code = nodecodes.par
97local penalty_code = nodecodes.penalty
98
99local parfillskip_code = gluecodes.parfillskip
100local parfillleftskip_code = gluecodes.parfillleftskip
101
102local dirvalues = nodes.dirvalues
103local lefttoright_code = dirvalues.lefttoright
104local righttoleft_code = dirvalues.righttoleft
105
106local maximum_stack = 0xFF
107
108local a_directions = attributes.private('directions')
109
110local directions = typesetters.directions
111local setcolor = directions.setcolor
112local getfences = directions.getfences
113
114local remove_controls = true directives.register("typesetters.directions.removecontrols",function(v) remove_controls = v end)
115
116
117local report_directions = logs.reporter("typesetting","directions three")
118
119local trace_directions = false trackers.register("typesetters.directions", function(v) trace_directions = v end)
120local trace_details = false trackers.register("typesetters.directions.details", function(v) trace_details = v end)
121local trace_list = false trackers.register("typesetters.directions.list", function(v) trace_list = v end)
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171local whitespace = {
172 lre = true,
173 rle = true,
174 lro = true,
175 rlo = true,
176 pdf = true,
177 bn = true,
178 ws = true,
179}
180
181local b_s_ws_on = {
182 b = true,
183 s = true,
184 ws = true,
185 on = true
186}
187
188
189
190local function show_list(list,size,what)
191 local what = what or "direction"
192 local joiner = utfchar(0x200C)
193 local result = { }
194 for i=1,size do
195 local entry = list[i]
196 local character = entry.char
197 local direction = entry[what]
198 if character == 0xFFFC then
199 local first = entry.id
200 local last = entry.last
201 local skip = entry.skip
202 if last then
203 result[i] = formatters["%-3s:%s %s..%s (%i)"](direction,joiner,nodecodes[first],nodecodes[last],skip or 0)
204 else
205 result[i] = formatters["%-3s:%s %s (%i)"](direction,joiner,nodecodes[first],skip or 0)
206 end
207 elseif character >= 0x202A and character <= 0x202C then
208 result[i] = formatters["%-3s:%s %U"](direction,joiner,character)
209 else
210 result[i] = formatters["%-3s:%s %c %U"](direction,joiner,character,character)
211 end
212 end
213 return concat(result,joiner .. " | " .. joiner)
214end
215
216
217
218local function show_done(list,size)
219 local joiner = utfchar(0x200C)
220 local result = { }
221 local format = formatters["<%s>"]
222 for i=1,size do
223 local entry = list[i]
224 local character = entry.char
225 local begindir = entry.begindir
226 local enddir = entry.enddir
227 if begindir then
228 result[#result+1] = format(begindir)
229 end
230 if entry.remove then
231
232 elseif character == 0xFFFC then
233 result[#result+1] = format("?")
234 elseif character == 0x0020 then
235 result[#result+1] = format(" ")
236 elseif character >= 0x202A and character <= 0x202C then
237 result[#result+1] = format(entry.original)
238 else
239 result[#result+1] = utfchar(character)
240 end
241 if enddir then
242 result[#result+1] = format(enddir)
243 end
244 end
245 return concat(result,joiner)
246end
247
248
249
250
251
252
253
254
255local mt_space = { __index = { char = 0x0020, direction = "ws", original = "ws", level = 0, skip = 0 } }
256local mt_lre = { __index = { char = 0x202A, direction = "lre", original = "lre", level = 0, skip = 0 } }
257local mt_rle = { __index = { char = 0x202B, direction = "rle", original = "rle", level = 0, skip = 0 } }
258local mt_pdf = { __index = { char = 0x202C, direction = "pdf", original = "pdf", level = 0, skip = 0 } }
259local mt_object = { __index = { char = 0xFFFC, direction = "on", original = "on", level = 0, skip = 0 } }
260
261local stack = table.setmetatableindex("table")
262local list = { }
263
264local function build_list(head,where)
265
266 local current = head
267 local size = 0
268 while current do
269 size = size + 1
270 local id = getid(current)
271 local p = properties[current]
272 if p and p.directions then
273
274 local skip = 0
275 local last = id
276 current = getnext(current)
277 while current do
278 local id = getid(current)
279 local p = properties[current]
280 if p and p.directions then
281 skip = skip + 1
282 last = id
283 current = getnext(current)
284 else
285 break
286 end
287 end
288 if id == last then
289 list[size] = setmetatable({ skip = skip, id = id },mt_object)
290 else
291 list[size] = setmetatable({ skip = skip, id = id, last = last },mt_object)
292 end
293 elseif id == glyph_code then
294 local chr = getchar(current)
295 local dir = directiondata[chr]
296
297 list[size] = { char = chr, direction = dir, original = dir, level = 0 }
298 current = getnext(current)
299
300 elseif id == glue_code then
301 list[size] = setmetatable({ },mt_space)
302 current = getnext(current)
303 elseif id == dir_code then
304 local dir, pop = getdirection(current)
305 if dir == lefttoright_code then
306 list[size] = setmetatable({ },pop and mt_pdf or mt_lre)
307 elseif dir == righttoleft_code then
308 list[size] = setmetatable({ },pop and mt_pdf or mt_rle)
309 else
310 list[size] = setmetatable({ id = id },mt_object)
311 end
312 current = getnext(current)
313 elseif id == math_code then
314 local skip = 0
315 current = getnext(current)
316 while getid(current) ~= math_code do
317 skip = skip + 1
318 current = getnext(current)
319 end
320 skip = skip + 1
321 current = getnext(current)
322 list[size] = setmetatable({ id = id, skip = skip },mt_object)
323 else
324 local skip = 0
325 local last = id
326 current = getnext(current)
327 while n do
328 local id = getid(current)
329 if id ~= glyph_code and id ~= glue_code and id ~= dir_code then
330 skip = skip + 1
331 last = id
332 current = getnext(current)
333 else
334 break
335 end
336 end
337 if id == last then
338 list[size] = setmetatable({ id = id, skip = skip },mt_object)
339 else
340 list[size] = setmetatable({ id = id, skip = skip, last = last },mt_object)
341 end
342 end
343 end
344 return list, size
345end
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361local fencestack = table.setmetatableindex("table")
362
363local function resolve_fences(list,size,start,limit)
364
365 local nofstack = 0
366 for i=start,limit do
367 local entry = list[i]
368 if entry.direction == "on" then
369 local char = entry.char
370 local mirror = mirrordata[char]
371 if mirror then
372 local class = textclassdata[char]
373 entry.mirror = mirror
374 entry.class = class
375 if class == "open" then
376 nofstack = nofstack + 1
377 local stacktop = fencestack[nofstack]
378 stacktop[1] = mirror
379 stacktop[2] = i
380 elseif nofstack == 0 then
381
382 elseif class == "close" then
383 while nofstack > 0 do
384 local stacktop = fencestack[nofstack]
385 if stacktop[1] == char then
386 local open = stacktop[2]
387 local close = i
388 list[open ].paired = close
389 list[close].paired = open
390 break
391 else
392
393 end
394 nofstack = nofstack - 1
395 end
396 end
397 end
398 end
399 end
400end
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417local function get_baselevel(head,list,size,direction)
418 if direction == lefttoright_code or direction == righttoleft_code then
419 return direction, true
420 elseif getid(head) == par_code and startofpar(head) then
421 direction = getdirection(head)
422 if direction == lefttoright_code or direction == righttoleft_code then
423 return direction, true
424 end
425 end
426
427 for i=1,size do
428 local entry = list[i]
429 local direction = entry.direction
430 if direction == "r" or direction == "al" then
431 return righttoleft_code, true
432 elseif direction == "l" then
433 return lefttoright_code, true
434 end
435 end
436 return lefttoright_code, false
437end
438
439local function resolve_explicit(list,size,baselevel)
440
441
442 local level = baselevel
443 local override = "on"
444 local nofstack = 0
445 for i=1,size do
446 local entry = list[i]
447 local direction = entry.direction
448
449 if direction == "rle" then
450 if nofstack < maximum_stack then
451 nofstack = nofstack + 1
452 local stacktop = stack[nofstack]
453 stacktop[1] = level
454 stacktop[2] = override
455 level = level + (level % 2 == 1 and 2 or 1)
456 override = "on"
457 entry.level = level
458 entry.direction = "bn"
459 entry.remove = true
460 elseif trace_directions then
461 report_directions("stack overflow at position %a with direction %a",i,direction)
462 end
463
464 elseif direction == "lre" then
465 if nofstack < maximum_stack then
466 nofstack = nofstack + 1
467 local stacktop = stack[nofstack]
468 stacktop[1] = level
469 stacktop[2] = override
470 level = level + (level % 2 == 1 and 1 or 2)
471 override = "on"
472 entry.level = level
473 entry.direction = "bn"
474 entry.remove = true
475 elseif trace_directions then
476 report_directions("stack overflow at position %a with direction %a",i,direction)
477 end
478
479 elseif direction == "rlo" then
480 if nofstack < maximum_stack then
481 nofstack = nofstack + 1
482 local stacktop = stack[nofstack]
483 stacktop[1] = level
484 stacktop[2] = override
485 level = level + (level % 2 == 1 and 2 or 1)
486 override = "r"
487 entry.level = level
488 entry.direction = "bn"
489 entry.remove = true
490 elseif trace_directions then
491 report_directions("stack overflow at position %a with direction %a",i,direction)
492 end
493
494 elseif direction == "lro" then
495 if nofstack < maximum_stack then
496 nofstack = nofstack + 1
497 local stacktop = stack[nofstack]
498 stacktop[1] = level
499 stacktop[2] = override
500 level = level + (level % 2 == 1 and 1 or 2)
501 override = "l"
502 entry.level = level
503 entry.direction = "bn"
504 entry.remove = true
505 elseif trace_directions then
506 report_directions("stack overflow at position %a with direction %a",i,direction)
507 end
508
509 elseif direction == "pdf" then
510 if nofstack > 0 then
511 local stacktop = stack[nofstack]
512 level = stacktop[1]
513 override = stacktop[2]
514 nofstack = nofstack - 1
515 entry.level = level
516 entry.direction = "bn"
517 entry.remove = true
518 elseif trace_directions then
519 report_directions("stack underflow at position %a with direction %a",
520 i, direction)
521 else
522 report_directions("stack underflow at position %a with direction %a: %s",
523 i, direction, show_list(list,size))
524 end
525
526 else
527 entry.level = level
528 if override ~= "on" then
529 entry.direction = override
530 end
531 end
532 end
533
534end
535
536local function resolve_weak(list,size,start,limit,orderbefore,orderafter)
537
538
539 for i=start,limit do
540 local entry = list[i]
541 if entry.direction == "nsm" then
542 if i == start then
543 entry.direction = orderbefore
544 else
545 entry.direction = list[i-1].direction
546 end
547 end
548 end
549
550
551
552 for i=start,limit do
553 local entry = list[i]
554 if entry.direction == "en" then
555 for j=i-1,start,-1 do
556 local prev = list[j]
557 local direction = prev.direction
558 if direction == "al" then
559 entry.direction = "an"
560 break
561 elseif direction == "r" or direction == "l" then
562 break
563 end
564 end
565 end
566 end
567
568
569
570 for i=start,limit do
571 local entry = list[i]
572 if entry.direction == "al" then
573 entry.direction = "r"
574 end
575 end
576
577
578
579
580 if false then
581 for i=start+1,limit-1 do
582 local entry = list[i]
583 local direction = entry.direction
584 if direction == "es" then
585 if list[i-1].direction == "en" and list[i+1].direction == "en" then
586 entry.direction = "en"
587 end
588 elseif direction == "cs" then
589 local prevdirection = list[i-1].direction
590 if prevdirection == "en" then
591 if list[i+1].direction == "en" then
592 entry.direction = "en"
593 end
594 elseif prevdirection == "an" and list[i+1].direction == "an" then
595 entry.direction = "an"
596 end
597 end
598 end
599 else
600 local runner = start + 2
601 if runner <= limit then
602 local before = list[start]
603 local current = list[start + 1]
604 local after = list[runner]
605 while after do
606 local direction = current.direction
607 if direction == "es" then
608 if before.direction == "en" and after.direction == "en" then
609 current.direction = "en"
610 end
611 elseif direction == "cs" then
612 local prevdirection = before.direction
613 if prevdirection == "en" then
614 if after.direction == "en" then
615 current.direction = "en"
616 end
617 elseif prevdirection == "an" and after.direction == "an" then
618 current.direction = "an"
619 end
620 end
621 before = current
622 current = after
623 after = list[runner]
624 runner = runner + 1
625 end
626 end
627 end
628
629
630
631 local i = start
632 while i <= limit do
633 if list[i].direction == "et" then
634 local runstart = i
635 local runlimit = runstart
636 for i=runstart,limit do
637 if list[i].direction == "et" then
638 runlimit = i
639 else
640 break
641 end
642 end
643 local rundirection = runstart == start and sor or list[runstart-1].direction
644 if rundirection ~= "en" then
645 rundirection = runlimit == limit and orderafter or list[runlimit+1].direction
646 end
647 if rundirection == "en" then
648 for j=runstart,runlimit do
649 list[j].direction = "en"
650 end
651 end
652 i = runlimit
653 end
654 i = i + 1
655 end
656
657
658
659 for i=start,limit do
660 local entry = list[i]
661 local direction = entry.direction
662 if direction == "es" or direction == "et" or direction == "cs" then
663 entry.direction = "on"
664 end
665 end
666
667
668 for i=start,limit do
669 local entry = list[i]
670 if entry.direction == "en" then
671 local prev_strong = orderbefore
672 for j=i-1,start,-1 do
673 local direction = list[j].direction
674 if direction == "l" or direction == "r" then
675 prev_strong = direction
676 break
677 end
678 end
679 if prev_strong == "l" then
680 entry.direction = "l"
681 end
682 end
683 end
684end
685
686local function resolve_neutral(list,size,start,limit,orderbefore,orderafter)
687
688 local i = start
689 while i <= limit do
690 local entry = list[i]
691 if b_s_ws_on[entry.direction] then
692
693 local leading_direction, trailing_direction, resolved_direction
694 local runstart = i
695 local runlimit = runstart
696
697 for j=runstart+1,limit do
698 if b_s_ws_on[list[j].direction] then
699
700 runlimit = j
701 else
702 break
703 end
704 end
705 if runstart == start then
706 leading_direction = orderbefore
707 else
708 leading_direction = list[runstart-1].direction
709 if leading_direction == "en" or leading_direction == "an" then
710 leading_direction = "r"
711 end
712 end
713 if runlimit == limit then
714 trailing_direction = orderafter
715 else
716 trailing_direction = list[runlimit+1].direction
717 if trailing_direction == "en" or trailing_direction == "an" then
718 trailing_direction = "r"
719 end
720 end
721 if leading_direction == trailing_direction then
722
723 resolved_direction = leading_direction
724 else
725
726 resolved_direction = entry.level % 2 == 1 and "r" or "l"
727 end
728 for j=runstart,runlimit do
729 list[j].direction = resolved_direction
730 end
731 i = runlimit
732 end
733 i = i + 1
734 end
735end
736
737local function resolve_implicit(list,size,start,limit,orderbefore,orderafter,baselevel)
738 for i=start,limit do
739 local entry = list[i]
740 local level = entry.level
741 local direction = entry.direction
742 if level % 2 ~= 1 then
743
744 if direction == "r" then
745 entry.level = level + 1
746 elseif direction == "an" or direction == "en" then
747 entry.level = level + 2
748 end
749 else
750
751 if direction == "l" or direction == "en" or direction == "an" then
752 entry.level = level + 1
753 end
754 end
755 end
756end
757
758local function resolve_levels(list,size,baselevel,analyze_fences)
759
760 local start = 1
761 while start < size do
762 local level = list[start].level
763 local limit = start + 1
764 while limit < size and list[limit].level == level do
765 limit = limit + 1
766 end
767 local prev_level = start == 1 and baselevel or list[start-1].level
768 local next_level = limit == size and baselevel or list[limit+1].level
769 local orderbefore = (level > prev_level and level or prev_level) % 2 == 1 and "r" or "l"
770 local orderafter = (level > next_level and level or next_level) % 2 == 1 and "r" or "l"
771
772 resolve_weak(list,size,start,limit,orderbefore,orderafter)
773
774 if analyze_fences then
775 resolve_fences(list,size,start,limit)
776 end
777
778 resolve_neutral(list,size,start,limit,orderbefore,orderafter)
779
780 resolve_implicit(list,size,start,limit,orderbefore,orderafter,baselevel)
781 start = limit
782 end
783
784 for i=1,size do
785 local entry = list[i]
786 local direction = entry.original
787
788 if direction == "s" or direction == "b" then
789 entry.level = baselevel
790
791 for j=i-1,1,-1 do
792 local entry = list[j]
793 if whitespace[entry.original] then
794 entry.level = baselevel
795 else
796 break
797 end
798 end
799 end
800 end
801
802 for i=size,1,-1 do
803 local entry = list[i]
804 if whitespace[entry.original] then
805 entry.level = baselevel
806 else
807 break
808 end
809 end
810
811 if analyze_fences then
812 for i=1,size do
813 local entry = list[i]
814 if entry.level % 2 == 1 then
815 if entry.mirror and not entry.paired then
816 entry.mirror = false
817 end
818
819 elseif entry.mirror then
820 entry.mirror = false
821 end
822 end
823 else
824 for i=1,size do
825 local entry = list[i]
826 if entry.level % 2 == 1 then
827 local mirror = mirrordata[entry.char]
828 if mirror then
829 entry.mirror = mirror
830 end
831 end
832 end
833 end
834end
835
836local stack = { }
837
838local function insert_dir_points(list,size)
839
840
841 local maxlevel = 0
842 local toggle = true
843 for i=1,size do
844 local level = list[i].level
845 if level > maxlevel then
846 maxlevel = level
847 end
848 end
849 for level=0,maxlevel do
850 local started
851 local begindir
852 local enddir
853 local prev
854 if toggle then
855 begindir = lefttoright_code
856 enddir = lefttoright_code
857 toggle = false
858 else
859 begindir = righttoleft_code
860 enddir = righttoleft_code
861 toggle = true
862 end
863 for i=1,size do
864 local entry = list[i]
865 if entry.level >= level then
866 if not started then
867 entry.begindir = begindir
868 started = true
869 end
870 else
871 if started then
872 prev.enddir = enddir
873 started = false
874 end
875 end
876 prev = entry
877 end
878 end
879
880 local last = list[size]
881 if not last.enddir then
882 local n = 0
883 for i=1,size do
884 local entry = list[i]
885 local e = entry.enddir
886 local b = entry.begindir
887 if e then
888 n = n - 1
889 end
890 if b then
891 n = n + 1
892 stack[n] = b
893 end
894 end
895 if n > 0 then
896 if trace_list and n > 1 then
897 report_directions("unbalanced list")
898 end
899 last.enddir = stack[n]
900 end
901 end
902end
903
904
905
906
907
908local function apply_to_list(list,size,head,pardir)
909 local index = 1
910 local current = head
911 if trace_list then
912 report_directions("start run")
913 end
914 while current do
915 if index > size then
916 report_directions("fatal error, size mismatch")
917 break
918 end
919 local id = getid(current)
920 local entry = list[index]
921 local begindir = entry.begindir
922 local enddir = entry.enddir
923 local p = properties[current]
924 if p then
925 p.directions = true
926 else
927 properties[current] = { directions = true }
928 end
929 if id == glyph_code then
930 local mirror = entry.mirror
931 if mirror then
932 setchar(current,mirror)
933 end
934 if trace_directions then
935 local direction = entry.direction
936 if trace_list then
937 local original = entry.original
938 local char = entry.char
939 local level = entry.level
940 if direction == original then
941 report_directions("%2i : %C : %s",level,char,direction)
942 else
943 report_directions("%2i : %C : %s -> %s",level,char,original,direction)
944 end
945 end
946 setcolor(current,direction,false,mirror)
947 end
948 elseif id == hlist_code or id == vlist_code then
949 setdirection(current,pardir)
950 elseif id == glue_code then
951
952 if enddir and getsubtype(current) == parfillskip_code then
953
954 local c = current
955 local p = getprev(c)
956 if p and getid(p) == glue_code and getsubtype(p) == parfillleftskip_code then
957 c = p
958 p = getprev(c)
959 end
960 if p and getid(p) == penalty_code then
961 c = p
962 end
963
964 head = insertnodebefore(head,c,new_direction(enddir,true))
965 enddir = false
966 end
967 elseif begindir then
968 if id == par_code and startofpar(current) then
969
970 head, current = insertnodeafter(head,current,new_direction(begindir))
971 begindir = nil
972 end
973 end
974 if begindir then
975 head = insertnodebefore(head,current,new_direction(begindir))
976 end
977 local skip = entry.skip
978 if skip and skip > 0 then
979 for i=1,skip do
980 current = getnext(current)
981 local p = properties[current]
982 if p then
983 p.directions = true
984 else
985 properties[current] = { directions = true }
986 end
987 end
988 end
989 if enddir then
990 head, current = insertnodeafter(head,current,new_direction(enddir,true))
991 end
992 if not entry.remove then
993 current = getnext(current)
994 elseif remove_controls then
995
996 head, current = remove_node(head,current,true)
997 else
998 current = getnext(current)
999 end
1000 index = index + 1
1001 end
1002 if trace_list then
1003 report_directions("stop run")
1004 end
1005 return head
1006end
1007
1008
1009
1010
1011
1012
1013
1014local function process(head,direction,only_one,where)
1015
1016 local attr = getattr(head,a_directions)
1017 local analyze_fences = getfences(attr)
1018
1019 local list, size = build_list(head,where)
1020 local baselevel, dirfound = get_baselevel(head,list,size,direction)
1021 if trace_details then
1022 report_directions("analyze: baselevel %a",baselevel == righttoleft_code and "r2l" or "l2r")
1023 report_directions("before : %s",show_list(list,size,"original"))
1024 end
1025 resolve_explicit(list,size,baselevel)
1026 resolve_levels(list,size,baselevel,analyze_fences)
1027 insert_dir_points(list,size)
1028 if trace_details then
1029 report_directions("after : %s",show_list(list,size,"direction"))
1030 report_directions("result : %s",show_done(list,size))
1031 end
1032 return apply_to_list(list,size,head,baselevel)
1033end
1034
1035local variables = interfaces.variables
1036
1037directions.installhandler(variables.one, process)
1038directions.installhandler(variables.two, process)
1039directions.installhandler(variables.three, process)
1040directions.installhandler(variables.unicode,process)
1041 |