texdirections.c /size: 7655 b    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5/*tex
6
7    In \LUATEX\ we started with the \OMEGA\ direction model, although only a handful of directions
8    is supported there (four to be precise). For l2r and r2l typesetting the frontend can basically
9    ignore directions. Only the font handler needs to be direction aware. The vertical directions in
10    \LUATEX\ demand swapping height and width occasionally when doing calculations. In the end it is
11    the backend code that does the hard work.
12
13    In the end, in \LUAMETATEX\ we only kept the horizontal directions. The vertical ones were not
14    really useful and didn't even work well. It's up to the macro package to cook up proper
15    solutions. The simplification (and rewrite) of the code also resulted in a more advanced box
16    model (with rotation and offsets) that can help implementing vertical rendering, but that code
17    is not here.
18
19    Todo: |\tracingdirections| but costly so not really. The tracinglist is a middleground but I 
20    might comment it at some point. 
21
22*/
23
24# include "luametatex.h"
25
26dir_state_info lmt_dir_state = {
27    .text_dir_ptr = null,
28    .padding      = 0,
29};
30
31/*tex The next two are used by the linebreak routine; they could be macros. */
32
33inline static halfword tex_aux_push_dir_node(halfword p, halfword d)
34{
35    halfword n = tex_copy_node(d);
36    node_next(n) = p;
37    return n;
38}
39
40inline static halfword tex_aux_pop_dir_node(halfword p)
41{
42    halfword n = node_next(p);
43    tex_flush_node(p);
44    return n;
45}
46
47halfword tex_update_dir_state(halfword p, halfword initial)
48{
49    if (node_subtype(p) == normal_dir_subtype) {
50        lmt_linebreak_state.dir_ptr = tex_aux_push_dir_node(lmt_linebreak_state.dir_ptr, p);
51        return dir_direction(p);
52    } else {
53        lmt_linebreak_state.dir_ptr = tex_aux_pop_dir_node(lmt_linebreak_state.dir_ptr);
54        if (lmt_linebreak_state.dir_ptr) {
55            return dir_direction(lmt_linebreak_state.dir_ptr);
56        } else {
57            return initial;
58        }
59    }
60}
61
62/*tex 
63    The next function runs over the whole list (|first|, |last|) and initial is normally the 
64    direction of the paragraph.  
65*/
66
67halfword tex_sanitize_dir_state(halfword first, halfword last, halfword initial)
68{
69    for (halfword e = first; e && e != last; e = node_next(e)) {
70        if (node_type(e) == dir_node) {
71            if (node_subtype(e) == normal_dir_subtype) {
72                lmt_linebreak_state.dir_ptr = tex_aux_push_dir_node(lmt_linebreak_state.dir_ptr, e);
73            } else if (lmt_linebreak_state.dir_ptr && dir_direction(lmt_linebreak_state.dir_ptr) == dir_direction(e)) {
74                /*tex A bit strange test. */
75                lmt_linebreak_state.dir_ptr = tex_aux_pop_dir_node(lmt_linebreak_state.dir_ptr);
76            }
77        }
78    }
79    if (lmt_linebreak_state.dir_ptr) {
80        return dir_direction(lmt_linebreak_state.dir_ptr);
81    } else {
82        return initial;
83    }
84}
85
86/*tex
87    Here we inject the nodes that inititialize and cancel the direction states as stored in the 
88    (reverse) stack into the list, after |tail|. 
89*/
90
91void tex_append_dir_state(void)
92{
93    halfword dir = lmt_dir_state.text_dir_ptr;
94    halfword tail = cur_list.tail;    
95    halfword first = null;
96    halfword last = null;
97    if (tracing_paragraph_lists) {
98        tex_begin_diagnostic();
99        tex_print_format("[paragraph: dirstate]");
100        tex_show_box(dir);
101        tex_end_diagnostic();
102    }
103    while (dir) {
104        if ((node_next(dir)) || (dir_direction(dir) != par_direction_par)) {
105            halfword tmp = tex_new_dir(normal_dir_subtype, dir_direction(dir));
106            tex_attach_attribute_list_copy(tmp, tail);
107            tex_try_couple_nodes(tmp, first);
108            first = tmp; 
109            if (! last) {
110                last = tmp; 
111            } 
112        }
113        dir = node_next(dir);
114    }
115    if (first) { 
116        if (tracing_paragraph_lists) {
117            tex_begin_diagnostic();
118            tex_print_format("[paragraph: injected dirs]");
119            tex_show_box(first);
120            tex_end_diagnostic();
121        }
122        tex_couple_nodes(cur_list.tail, first);
123        cur_list.tail = last; 
124    }
125}
126
127halfword tex_complement_dir_state(halfword tail)
128{
129    halfword aftertail = node_next(tail);
130    for (halfword topdir = lmt_linebreak_state.dir_ptr; topdir ; topdir = node_next(topdir)) {
131        halfword dir = tex_new_dir(cancel_dir_subtype, dir_direction(topdir));
132        tex_attach_attribute_list_copy(dir, tail);
133        tex_couple_nodes(tail, dir);
134        tex_try_couple_nodes(dir, aftertail);
135        tail = dir;
136    }
137    return tail;
138}
139
140void tex_initialize_directions(void)
141{
142    lmt_dir_state.text_dir_ptr = tex_new_dir(normal_dir_subtype, direction_def_value);
143}
144
145void tex_cleanup_directions(void)
146{
147    tex_flush_node(lmt_dir_state.text_dir_ptr); /* tex_free_node(lmt_dir_state.text_dir_ptr, dir_node_size) */
148}
149
150halfword tex_new_dir(quarterword subtype, halfword direction)
151{
152    halfword p = tex_new_node(dir_node, subtype);
153    dir_direction(p) = direction;
154    dir_level(p) = cur_level;
155    return p;
156}
157
158void tex_push_text_dir_ptr(halfword val)
159{
160    if (tracing_direction_lists) {
161        tex_begin_diagnostic();
162        tex_print_format("[direction: push text, level %i, before]", cur_level);
163        tex_show_box(lmt_dir_state.text_dir_ptr);
164        tex_end_diagnostic();
165    }
166    if (dir_level(lmt_dir_state.text_dir_ptr) == cur_level) {
167        /*tex update */
168        dir_direction(lmt_dir_state.text_dir_ptr) = val;
169    } else {
170        /*tex we push in front of head */
171        halfword text_dir_tmp = tex_new_dir(normal_dir_subtype, val);
172        node_next(text_dir_tmp) = lmt_dir_state.text_dir_ptr;
173        lmt_dir_state.text_dir_ptr = text_dir_tmp;
174    }
175    if (tracing_direction_lists) {
176        tex_begin_diagnostic();
177        tex_print_format("[direction: push text, level %i, after]", cur_level);
178        tex_show_box(lmt_dir_state.text_dir_ptr);
179        tex_end_diagnostic();
180    }
181}
182
183void tex_pop_text_dir_ptr(void)
184{
185    halfword text_dir_ptr = lmt_dir_state.text_dir_ptr;
186    if (tracing_direction_lists) {
187        tex_begin_diagnostic();
188        tex_print_format("[direction: pop text, level %i, before]", cur_level);
189        tex_show_box(lmt_dir_state.text_dir_ptr);
190        tex_end_diagnostic();
191    }
192    if (dir_level(text_dir_ptr) == cur_level) { // maybe > and whole chain 
193        /*tex we remove from the head */
194        halfword text_dir_tmp = node_next(text_dir_ptr);
195        tex_flush_node(text_dir_ptr);
196        lmt_dir_state.text_dir_ptr = text_dir_tmp;
197    }
198    if (tracing_direction_lists) {
199        tex_begin_diagnostic();
200        tex_print_format("[direction: pop text, level %i, after]", cur_level);
201        tex_show_box(lmt_dir_state.text_dir_ptr);
202        tex_end_diagnostic();
203    }
204}
205
206void tex_set_math_dir(halfword d)
207{
208    if (valid_direction(d)) {
209        update_tex_math_direction(d);
210    }
211}
212
213void tex_set_par_dir(halfword d)
214{
215    if (valid_direction(d)) {
216        update_tex_par_direction(d);
217    }
218}
219
220void tex_set_text_dir(halfword d)
221{
222    if (valid_direction(d)) {
223        tex_inject_text_or_line_dir(d, 0);
224        update_tex_text_direction(d);
225        update_tex_internal_dir_state(internal_dir_state_par + 1);
226    }
227}
228
229void tex_set_line_dir(halfword d)
230{
231    if (valid_direction(d)) {
232        tex_inject_text_or_line_dir(d, 1);
233        update_tex_text_direction(d);
234        update_tex_internal_dir_state(internal_dir_state_par + 1);
235    }
236}
237
238void tex_set_box_dir(halfword b, singleword d)
239{
240    if (valid_direction(d)) {
241        box_dir(box_register(b)) = (singleword) d;
242    }
243}
244