1
2
3
4
5
6
7
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Characters and spaces in horizontal mode (*TeX: The Program*
//! §1034–§1044, LuaTeX style).
//!
//! In LuaTeX a character simply becomes a glyph node: ligatures and kerns
//! are applied later (see [`crate::nodeproc`]). The space factor is
//! maintained as in TeX82. Interword glue made from the font or from
//! `\spaceskip` has subtype `spaceskip`; glue from `\xspaceskip` has
//! subtype `xspaceskip`.
use crate::Engine;
use crate::cmd::Cmd;
use crate::glue::Glue;
use crate::nodes::glue_sub;
use crate::primitives_gen::{assign_glue as gp, assign_int as ip};
/// Whether a glue specification is zero (TeX's `zero_glue` test).
#[inline]
fn is_zero(g: &Glue) -> bool {
g.width == 0 && g.stretch == 0 && g.shrink == 0
}
impl Engine {
/// Characters in horizontal mode: appends glyph nodes for the current
/// character and the characters that follow; on return the next
/// non-character token is current (TeX's main loop, `goto reswitch`).
pub fn main_loop_char(&mut self) {
let tracing = self.eqtb.int(ip::TRACINGCOMMANDS) > 0;
loop {
self.append_char(self.cur_chr);
if tracing {
// LuaTeX reports every character when tracing commands.
self.get_x_token();
return;
}
self.get_next();
if matches!(self.cur_cmd, Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven) {
continue;
}
self.x_token();
match self.cur_cmd {
Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven => continue,
Cmd::CharNum => {
self.scan_char_num();
self.cur_chr = self.cur_val as u32;
continue;
}
_ => return,
}
}
}
/// Appends one character in the current font and adjusts the space
/// factor (`adjust_space_factor`).
#[inline]
fn append_char(&mut self, c: u32) {
let s = self.eqtb.sf.get(c) as i32;
let cl = self.cur_list_mut();
if s == 1000 {
cl.space_factor = 1000;
} else if s < 1000 {
if s > 0 {
cl.space_factor = s;
}
} else if cl.space_factor < 1000 {
cl.space_factor = 1000;
} else {
cl.space_factor = s;
}
let f = self.eqtb.cur_font.val;
let g = self.new_char_node(f, c);
self.tail_append(g);
}
/// The interword glue of font `f` (`font_glue`).
fn font_glue(&self, f: u32) -> Glue {
let p = &self.font(f).params;
Glue {
width: p.get(2).copied().unwrap_or(0),
stretch: p.get(3).copied().unwrap_or(0),
shrink: p.get(4).copied().unwrap_or(0),
..Glue::ZERO
}
}
/// A space in horizontal mode.
pub fn app_space_main(&mut self) {
if self.cur_list().space_factor == 1000 {
self.append_normal_space();
} else {
self.app_space();
}
}
/// `\ ` (explicit space).
pub fn app_explicit_space(&mut self) {
self.append_normal_space();
}
/// Appends a normal interword space (§1041).
fn append_normal_space(&mut self) {
let ss = self.eqtb.glue(gp::SPACESKIP);
let g = if is_zero(&ss) {
self.font_glue(self.eqtb.cur_font.val)
} else {
ss
};
let p = self.new_glue_node(g, glue_sub::SPACE_SKIP);
self.tail_append(p);
}
/// Appends a space when the space factor is not 1000 (`app_space`).
fn app_space(&mut self) {
let sf = self.cur_list().space_factor;
let xs = self.eqtb.glue(gp::XSPACESKIP);
let p = if sf >= 2000 && !is_zero(&xs) {
self.new_glue_node(xs, glue_sub::XSPACE_SKIP)
} else {
let ss = self.eqtb.glue(gp::SPACESKIP);
let f = self.eqtb.cur_font.val;
let mut g = if is_zero(&ss) { self.font_glue(f) } else { ss };
if sf >= 2000 {
g.width += self.font(f).params.get(7).copied().unwrap_or(0);
}
g.stretch = retex_arith::xn_over_d(g.stretch, sf, 1000)
.map(|r| r.quotient)
.unwrap_or(g.stretch);
g.shrink = retex_arith::xn_over_d(g.shrink, 1000, sf)
.map(|r| r.quotient)
.unwrap_or(g.shrink);
self.new_glue_node(g, glue_sub::SPACE_SKIP)
};
self.tail_append(p);
}
}