texadjust.c /size: 20 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7typedef struct adjust_properties {
8    halfword options;
9    halfword code;
10    halfword index;
11    scaled   depthbefore;
12    scaled   depthafter;
13    halfword attrlist;
14} adjust_properties;
15
16typedef enum saved_adjust_entries {
17    saved_adjust_location_entry     = 0,
18    saved_adjust_options_entry      = 0,
19    saved_adjust_index_entry        = 0,
20    saved_adjust_attr_list_entry    = 1,
21    saved_adjust_depth_before_entry = 1,
22    saved_adjust_depth_after_entry  = 1,
23    saved_adjust_target_entry       = 2,
24    saved_adjust_n_of_records       = 3,
25} saved_adjust_entries;
26
27inline static void saved_adjust_initialize(void)
28{
29    saved_type(0) = saved_record_0;
30    saved_type(1) = saved_record_1;
31    saved_type(2) = saved_record_2;
32    saved_record(0) = adjust_save_type;
33    saved_record(1) = adjust_save_type;
34    saved_record(2) = adjust_save_type;
35}
36
37# define saved_adjust_location     saved_value_1(saved_adjust_location_entry)
38# define saved_adjust_options      saved_value_2(saved_adjust_options_entry)
39# define saved_adjust_index        saved_value_3(saved_adjust_index_entry)
40# define saved_adjust_attr_list    saved_value_1(saved_adjust_attr_list_entry)
41# define saved_adjust_depth_before saved_value_2(saved_adjust_depth_before_entry)
42# define saved_adjust_depth_after  saved_value_3(saved_adjust_depth_after_entry)
43# define saved_adjust_target       saved_value_1(saved_adjust_target_entry)
44
45void tex_show_adjust_group(void)
46{
47    tex_print_str_esc("vadjust");
48    if (saved_adjust_location == pre_adjust_code) {
49        tex_print_str(" pre");
50    }
51    if (saved_adjust_options & adjust_option_before) {
52        tex_print_str(" before");
53    }
54}
55
56int tex_show_adjust_record(void)
57{
58    tex_print_str("adjust ");
59    switch (saved_type(0)) { 
60       case saved_record_0:
61            tex_print_format("location %i, options %i, index %i", saved_value_1(0), saved_value_2(0), saved_value_3(0));
62            break;
63       case saved_record_1:
64            tex_print_format("attrlist %i, depth before %p, depth after %p", saved_value_1(0), saved_value_2(0), saved_value_3(0));
65            break;
66       case saved_record_2:
67            tex_print_format("target %i", saved_value_1(0));
68            break;
69       default: 
70            return 0;
71    }
72    return 1;
73}
74
75static void tex_scan_adjust_keys(adjust_properties *properties)
76{
77    properties->code = post_adjust_code;
78    properties->options = adjust_option_none;
79    properties->index = 0;
80    properties->depthbefore = 0;
81    properties->depthafter = 0;
82    properties->attrlist = null;
83    while (1) {
84        switch (tex_scan_character("abdipABDIP", 0, 1, 0)) {
85            case 'p': case 'P':
86                switch (tex_scan_character("roRO", 0, 0, 0)) {
87                    case 'r': case 'R':
88                        if (tex_scan_mandate_keyword("pre", 2)) {
89                            properties->code = pre_adjust_code;
90                        }
91                        break;
92                    case 'o': case 'O':
93                        if (tex_scan_mandate_keyword("post", 2)) {
94                            properties->code = post_adjust_code;
95                        }
96                        break;
97                    default:
98                        tex_aux_show_keyword_error("pre|post");
99                        goto DONE;
100                }
101                break;
102            case 'b': case 'B':
103                switch (tex_scan_character("aeAE", 0, 0, 0)) {
104                    case 'a': case 'A':
105                        if (tex_scan_mandate_keyword("baseline", 2)) {
106                            properties->options |= adjust_option_baseline;
107                        }
108                        break;
109                    case 'e': case 'E':
110                        if (tex_scan_mandate_keyword("before", 2)) {
111                            properties->options |= adjust_option_before;
112                        }
113                        break;
114                    default:
115                        tex_aux_show_keyword_error("baseline|before");
116                        goto DONE;
117               }
118                break;
119            case 'i': case 'I':
120                if (tex_scan_mandate_keyword("index", 1)) {
121                    properties->index = tex_scan_integer(0, NULL);
122                    if (! tex_valid_adjust_index(properties->index)) {
123                        properties->index = 0; /* for now no error */
124                    }
125                }
126                break;
127            case 'a': case 'A':
128                switch (tex_scan_character("ftFT", 0, 0, 0)) {
129                    case 'f': case 'F':
130                        if (tex_scan_mandate_keyword("after", 2)) {
131                            properties->options &= ~(adjust_option_before | properties->options);
132                        }
133                        break;
134                    case 't': case 'T':
135                        if (tex_scan_mandate_keyword("attr", 2)) {
136                            properties->attrlist = tex_scan_attribute(properties->attrlist);
137                        }
138                        break;
139                    default:
140                        tex_aux_show_keyword_error("after|attr");
141                        goto DONE;
142                }   
143                break;
144            case 'd': case 'D':
145                if (tex_scan_mandate_keyword("depth", 1)) {
146                    switch (tex_scan_character("abclABCL", 0, 1, 0)) { /* so a space is permitted */
147                        case 'a': case 'A':
148                            if (tex_scan_mandate_keyword("after", 1)) {
149                                properties->options |= adjust_option_depth_after;
150                                properties->depthafter = tex_scan_dimension(0, 0, 0, 0, NULL);
151                            }
152                            break;
153                        case 'b': case 'B':
154                            if (tex_scan_mandate_keyword("before", 1)) {
155                                properties->options |= adjust_option_depth_before;
156                                properties->depthbefore = tex_scan_dimension(0, 0, 0, 0, NULL);
157                            }
158                            break;
159                        case 'c': case 'C':
160                            if (tex_scan_mandate_keyword("check", 1)) {
161                                properties->options |= adjust_option_depth_check;
162                            }
163                            break;
164                        case 'l': case 'L':
165                            if (tex_scan_mandate_keyword("last", 1)) {
166                                properties->options |= adjust_option_depth_last;
167                            }
168                            break;
169                        default:
170                            tex_aux_show_keyword_error("after|before|check|last");
171                            goto DONE;
172                    }
173                }
174                break;
175            default:
176                goto DONE;
177        }
178    }
179  DONE:
180    if (! properties->attrlist) {
181        properties->attrlist = tex_current_attribute_list();
182        add_attribute_reference(properties->attrlist); /* needs checking */
183    }
184}
185
186int tex_valid_adjust_index(halfword n)
187{
188    return n >= 0;
189}
190
191void tex_set_vadjust(halfword target)
192{
193    adjust_properties properties;
194    tex_scan_adjust_keys(&properties);
195    saved_adjust_initialize();
196    saved_adjust_location = properties.code;
197    saved_adjust_options = properties.options;
198    saved_adjust_index = properties.index;
199    saved_adjust_attr_list = properties.attrlist;
200    saved_adjust_depth_before = properties.depthbefore;
201    saved_adjust_depth_after = properties.depthafter;
202    saved_adjust_target = target;
203    lmt_save_state.save_stack_data.ptr += saved_adjust_n_of_records;
204    tex_new_save_level(vadjust_group);
205    tex_scan_left_brace();
206    tex_normal_paragraph(vadjust_par_context);
207    tex_push_nest();
208    cur_list.mode = internal_vmode;
209    cur_list.prev_depth = ignore_depth_criterion_par;
210}
211
212void tex_run_vadjust(void)
213{
214    tex_set_vadjust(-1);
215}
216
217void tex_finish_vadjust_group(void)
218{
219    if (! tex_wrapped_up_paragraph(vadjust_par_context, 0)) {
220        halfword box, adjust, target; /*tex for short-term use */
221        tex_end_paragraph(vadjust_group, vadjust_par_context);
222        tex_unsave();
223        lmt_save_state.save_stack_data.ptr -= saved_adjust_n_of_records;
224        box = tex_vpack(node_next(cur_list.head), 0, packing_additional, max_dimension, direction_unknown, holding_none_option, NULL);
225        tex_pop_nest();
226        adjust = tex_new_node(adjust_node, (quarterword) saved_adjust_location);
227        target = saved_adjust_target;
228        adjust_list(adjust) = box_list(box);
229        adjust_options(adjust) = (halfword) saved_adjust_options;
230        adjust_index(adjust) = (halfword) saved_adjust_index;
231        adjust_depth_before(adjust) = (halfword) saved_adjust_depth_before;
232        adjust_depth_after(adjust) = (halfword) saved_adjust_depth_after;
233        tex_attach_attribute_list_attribute(adjust, (halfword) saved_adjust_attr_list);
234        if (target < 1) {
235            tex_tail_append(adjust);
236        } else { 
237            tex_adjust_attach(target, adjust);
238        }
239        box_list(box) = null;
240        tex_flush_node(box);
241        /* we never do the callback ... maybe move it outside */
242        if (target < 0 && lmt_nest_state.nest_data.ptr == 0) {
243            tex_build_page(vadjust_page_context, 0);
244        }
245    }
246}
247
248/*tex Append or prepend vadjust nodes. Here head is a temp node! */
249
250halfword tex_append_adjust_list(halfword head, halfword tail, halfword adjust, const char *detail)
251{
252    while (adjust && node_type(adjust) == adjust_node) {
253        halfword next = node_next(adjust);
254        if (tail == head) {
255            node_next(head) = adjust;
256        } else {
257            tex_couple_nodes(tail, adjust);
258        }
259        if (tracing_adjusts_par > 1) {
260            tex_begin_diagnostic();
261            tex_print_format("[adjust: index %i, location %s, append, %s]", adjust_index(adjust), tex_aux_subtype_str(adjust), detail);
262            tex_print_node_list(adjust_list(adjust), "adjust",show_box_depth_par, show_box_breadth_par);
263            tex_end_diagnostic();
264        }
265        tail = adjust;
266        adjust = next;
267    }
268    return tail;
269}
270
271halfword tex_prepend_adjust_list(halfword head, halfword tail, halfword adjust, const char *detail)
272{
273    while (adjust && node_type(adjust) == adjust_node) {
274        halfword next = node_next(adjust);
275        if (tail == head) {
276            node_next(head) = adjust;
277            tail = adjust;
278        } else {
279            tex_try_couple_nodes(adjust, node_next(node_next(head)));
280            tex_couple_nodes(node_next(head), adjust);
281        }
282        if (tracing_adjusts_par > 1) {
283            tex_begin_diagnostic();
284            tex_print_format("[adjust: index %i, location %s, prepend, %s]", adjust_index(adjust), tex_aux_subtype_str(adjust), detail);
285            tex_print_node_list(adjust_list(adjust), "adjust", show_box_depth_par, show_box_breadth_par);
286            tex_end_diagnostic();
287        }
288        adjust = next;
289    }
290    return tail;
291}
292
293void tex_inject_adjust_list(halfword adjust, int obeyoptions, halfword nextnode, const line_break_properties *properties)
294{
295    if (adjust && node_type(adjust) == temp_node) {
296        adjust = node_next(adjust);
297    }
298    while (adjust && node_type(adjust) == adjust_node) {
299        halfword next = node_next(adjust);
300        halfword list = adjust_list(adjust);
301        if (tracing_adjusts_par > 1) {
302            tex_begin_diagnostic();
303            tex_print_format("[adjust: index %i, location %s, inject]", adjust_index(adjust), tex_aux_subtype_str(adjust));
304            tex_print_node_list(adjust_list(adjust), "adjust", show_box_depth_par, show_box_breadth_par);
305            tex_end_diagnostic();
306        }
307        if (list) {
308            if (obeyoptions && has_adjust_option(adjust, adjust_option_baseline)) { 
309                /*tex
310                    Here we attach data to a line. On the todo is to prepend and append to 
311                    the lines (nicer when we number lines). 
312                */
313                if (node_type(list) == hlist_node || node_type(list) == vlist_node) {
314                    if (nextnode) { 
315                        /*tex 
316                            This is the |pre| case where |nextnode| is the line to be appended 
317                            after the adjust box |list|.
318                        */
319                        if (node_type(nextnode) == hlist_node || node_type(nextnode) == vlist_node) {
320                            if (box_height(nextnode) > box_height(list)) {
321                                box_height(list) = box_height(nextnode);
322                            }
323                            if (box_depth(list) > box_depth(nextnode)) {
324                                box_depth(nextnode) = box_depth(list);
325                            }
326                            /* not ok yet */
327                            box_y_offset(nextnode) += box_height(nextnode);
328                            tex_check_box_geometry(nextnode);
329                            /* till here */
330                            box_height(nextnode) = 0;
331                            box_depth(list) = 0;
332                        }
333                    } else { 
334                        /*tex 
335                            Here we have the |post| case where the line will end up before the 
336                            adjusted content.
337                        */
338                        halfword prevnode = cur_list.tail;
339                        if (node_type(prevnode) == hlist_node || node_type(prevnode) == vlist_node) {
340                            if (box_height(prevnode) < box_height(list)) {
341                                box_height(prevnode) = box_height(list);
342                            }
343                            if (box_depth(list) < box_depth(prevnode)) {
344                                box_depth(list) = box_depth(prevnode);
345                            }
346                            box_height(list) = 0;
347                            box_depth(prevnode) = 0;
348                        }
349                    }
350                }
351            }
352            if (obeyoptions && has_adjust_option(adjust, adjust_option_depth_before)) { 
353                cur_list.prev_depth = adjust_depth_before(adjust);
354            }
355            if (obeyoptions && has_adjust_option(adjust, adjust_option_depth_check)) { 
356                tex_append_to_vlist(list, -1, properties);
357            } else { 
358                tex_tail_append_list(list);
359             // tex_couple_nodes(prevnode, list);
360             // cur_list.tail = tex_tail_of_node_list(list);
361            }
362            if (obeyoptions && has_adjust_option(adjust, adjust_option_depth_after)) { 
363                cur_list.prev_depth = adjust_depth_after(adjust);
364            } else if (obeyoptions && has_adjust_option(adjust, adjust_option_depth_last)) { 
365                cur_list.prev_depth = box_depth(list);
366            }
367//    cur_list.tail = tex_tail_of_node_list(cur_list.tail);
368            if (! lmt_page_builder_state.output_active) {
369                lmt_append_line_filter_callback(post_adjust_append_line_context, adjust_index(adjust));
370            }
371            adjust_list(adjust) = null;
372        }
373        tex_flush_node(adjust);
374        adjust = next;
375    }
376}
377
378void tex_adjust_attach(halfword box, halfword adjust)
379{
380    if (adjust_list(adjust)) {
381        node_prev(adjust) = null;
382        node_next(adjust) = null;
383        if (tracing_adjusts_par > 1) {
384            tex_begin_diagnostic();
385            tex_print_format("[adjust: index %i, location %s, attach]", adjust_index(adjust), tex_aux_subtype_str(adjust));
386            tex_print_node_list(adjust_list(adjust), "attach",show_box_depth_par, show_box_breadth_par);
387            tex_end_diagnostic();
388        }
389        switch (node_subtype(adjust)) {
390            case pre_adjust_code:
391                if (! box_pre_adjusted(box)) {
392                    box_pre_adjusted(box) = adjust;
393                } else if (has_adjust_option(adjust, adjust_option_before)) {
394                    tex_couple_nodes(adjust, box_pre_adjusted(box));
395                    box_pre_adjusted(box) = adjust;
396                } else {
397                    tex_couple_nodes(tex_tail_of_node_list(box_pre_adjusted(box)), adjust);
398                }
399                node_subtype(adjust) = local_adjust_code;
400                break;
401            case post_adjust_code:
402                if (! box_post_adjusted(box)) {
403                    box_post_adjusted(box) = adjust;
404                } else if (has_adjust_option(adjust, adjust_option_before)) {
405                    tex_couple_nodes(adjust, box_post_adjusted(box));
406                    box_post_adjusted(box) = adjust;
407                } else {
408                    tex_couple_nodes(tex_tail_of_node_list(box_post_adjusted(box)), adjust);
409                }
410                node_subtype(adjust) = local_adjust_code;
411                break;
412            case local_adjust_code:
413                tex_normal_error("vadjust post", "unexpected local attach");
414                break;
415        }
416    } else {
417        tex_flush_node(adjust);
418    }
419}
420
421void tex_adjust_passon(halfword box, halfword adjust)
422{
423    halfword head = adjust ? adjust_list(adjust) : null;
424    (void) box;
425    if (head) {
426        node_prev(adjust) = null;
427        node_next(adjust) = null;
428        switch (node_subtype(adjust)) {
429            case pre_adjust_code:
430                if (lmt_packaging_state.pre_adjust_tail) {
431                    if (lmt_packaging_state.pre_adjust_tail != pre_adjust_head && has_adjust_option(adjust, adjust_option_before)) {
432                        lmt_packaging_state.pre_adjust_tail = tex_prepend_adjust_list(pre_adjust_head, lmt_packaging_state.pre_adjust_tail, adjust, "passon");
433                    } else {
434                        lmt_packaging_state.pre_adjust_tail = tex_append_adjust_list(pre_adjust_head, lmt_packaging_state.pre_adjust_tail, adjust, "passon");
435                    }
436                } else {
437                    tex_normal_error("vadjust pre", "invalid list");
438                }
439                break;
440            case post_adjust_code:
441                if (lmt_packaging_state.post_adjust_tail) {
442                    if (lmt_packaging_state.post_adjust_tail != post_adjust_head && has_adjust_option(adjust, adjust_option_before)) {
443                        lmt_packaging_state.post_adjust_tail = tex_prepend_adjust_list(post_adjust_head, lmt_packaging_state.post_adjust_tail, adjust, "passon");
444                    } else {
445                        lmt_packaging_state.post_adjust_tail = tex_append_adjust_list(post_adjust_head, lmt_packaging_state.post_adjust_tail, adjust, "passon");
446                    }
447                } else {
448                    tex_normal_error("vadjust post", "invalid list");
449                }
450                break;
451            case local_adjust_code:
452                tex_normal_error("vadjust post", "unexpected local passon");
453                break;
454        }
455    } else {
456        tex_flush_node(adjust);
457    }
458}
459
460static void tex_aux_show_flush_adjust(halfword adjust, const char *what, const char *detail)
461{
462    if (tracing_adjusts_par > 1) {
463        tex_begin_diagnostic();
464        tex_print_format("[adjust: index %i, location %s, flush, %s]", adjust_index(adjust), tex_aux_subtype_str(adjust), detail);
465        tex_print_node_list(adjust_list(adjust), what, show_box_depth_par, show_box_breadth_par);
466        tex_end_diagnostic();
467    }
468}
469
470halfword tex_flush_adjust_append(halfword adjust, halfword tail)
471{
472    while (adjust) {
473        halfword p = adjust;
474        halfword h = adjust_list(adjust);
475        if (h) {
476            int ishmode = is_h_mode(cur_list.mode);
477            tex_aux_show_flush_adjust(p, "append", ishmode ? "repack" : "direct");
478            if (ishmode) { 
479                halfword n = tex_new_node(adjust_node, post_adjust_code);
480                // tex_attach_attribute_list_copy(n, post_adjusted);
481                adjust_list(n) = h;
482                h = n;
483            }
484            tex_try_couple_nodes(tail, h);
485            tail = tex_tail_of_node_list(h);
486            adjust_list(p) = null;
487        }
488        adjust = node_next(p);
489        tex_flush_node(p);
490    }
491    return tail;
492}
493
494halfword tex_flush_adjust_prepend(halfword adjust, halfword tail)
495{
496    while (adjust) {
497        halfword p = adjust;
498        halfword h = adjust_list(adjust);
499        if (h) {
500            int ishmode = is_h_mode(cur_list.mode);
501            tex_aux_show_flush_adjust(p, "prepend", ishmode ? "repack" : "direct");
502            if (ishmode) { 
503                halfword n = tex_new_node(adjust_node, pre_adjust_code);
504                // tex_attach_attribute_list_copy(n, pre_adjusted);
505                adjust_list(n) = h;
506                h = n;
507            }
508            tex_try_couple_nodes(tail, h);
509            tail = tex_tail_of_node_list(h);
510            adjust_list(p) = null;
511        }
512        adjust = node_next(p);
513        tex_flush_node(p);
514    }
515    return tail;
516}
517
518void tex_initialize_adjust(void)
519{
520}
521
522void tex_cleanup_adjust(void)
523{
524}
525