use crate::Engine;
use crate::cmd::Cmd;
use crate::eqtb::{Loc, Val};
use crate::glue::{FIL, FILL, Glue};
use crate::input::TokKind;
use crate::maincontrol::{BoxContext, IGNORE_DEPTH, Mode, ModeKind};
use crate::nodes::{BoxData, NodeData, NodeRef, glue_sub, id, kern_sub};
use crate::primitives_gen::{assign_dimen as dp, assign_glue as gp, assign_int as ip};
use crate::save::{GroupCode, SaveEntry};
use crate::scan::ValLevel;
use crate::token::Token;
use crate::typeset::NULL_FLAG;
#[allow(missing_docs)]
pub mod chr {
pub const BOX: u32 = 0;
pub const COPY: u32 = 1;
pub const LAST_BOX: u32 = 2;
pub const VSPLIT: u32 = 3;
pub const TPACK: u32 = 4;
pub const VPACK: u32 = 5;
pub const HPACK: u32 = 6;
pub const VTOP: u32 = 7;
pub const VBOX: u32 = 8;
pub const HBOX: u32 = 141;
pub const SHIPOUT: u32 = 98;
pub const FIL: u32 = 0;
pub const FILL: u32 = 1;
pub const SS: u32 = 2;
pub const FIL_NEG: u32 = 3;
pub const SKIP: u32 = 4;
pub const MSKIP: u32 = 5;
pub const NOINDENT: u32 = 0;
pub const INDENT: u32 = 1;
pub const QUITVMODE: u32 = 2;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PackSpec {
Exactly(i32),
Additional(i32),
}
impl PackSpec {
pub const NATURAL: PackSpec = PackSpec::Additional(0);
}
const BOX_FLAG: i64 = 1 << 40;
const GLOBAL_BOX_FLAG: i64 = BOX_FLAG + 0x1_0000;
const SHIP_OUT_FLAG: i64 = BOX_FLAG + 0x2_0000;
const LEADER_FLAG: i64 = BOX_FLAG + 0x3_0000;
pub fn ctx_to_i64(c: BoxContext) -> i64 {
match c {
BoxContext::Shift(s) => i64::from(s),
BoxContext::SetBox(n) => BOX_FLAG + i64::from(n),
BoxContext::GlobalSetBox(n) => GLOBAL_BOX_FLAG + i64::from(n),
BoxContext::Shipout => SHIP_OUT_FLAG,
BoxContext::Leaders(s) => LEADER_FLAG + i64::from(s),
}
}
pub fn ctx_from_i64(v: i64) -> BoxContext {
if v < BOX_FLAG {
BoxContext::Shift(v as i32)
} else if v < GLOBAL_BOX_FLAG {
BoxContext::SetBox((v - BOX_FLAG) as u32)
} else if v < SHIP_OUT_FLAG {
BoxContext::GlobalSetBox((v - GLOBAL_BOX_FLAG) as u32)
} else if v == SHIP_OUT_FLAG {
BoxContext::Shipout
} else {
BoxContext::Leaders((v - LEADER_FLAG) as u16)
}
}
pub fn norm_min(h: i32) -> u8 {
h.clamp(1, 63) as u8
}
impl Engine {
pub fn push_saved(&mut self, v: i64) {
self.save.stack.push(SaveEntry::Value(v));
}
pub fn pop_saved(&mut self) -> i64 {
match self.save.stack.pop() {
Some(SaveEntry::Value(v)) => v,
other => {
if let Some(e) = other {
self.save.stack.push(e);
}
self.pop_saved_below()
}
}
}
#[cold]
fn pop_saved_below(&mut self) -> i64 {
let found = self
.save
.stack
.iter()
.rev()
.take_while(|e| !matches!(e, SaveEntry::Boundary { .. }))
.position(|e| matches!(e, SaveEntry::Value(_)));
match found {
Some(k) => {
let i = self.save.stack.len() - 1 - k;
match self.save.stack.remove(i) {
SaveEntry::Value(v) => v,
_ => 0,
}
}
None => {
debug_assert!(false, "save stack out of sync");
0
}
}
}
pub fn peek_saved(&self, k: usize) -> i64 {
let n = self.save.stack.len();
match self.save.stack.get(n.wrapping_sub(1 + k)) {
Some(SaveEntry::Value(v)) => *v,
_ => 0,
}
}
pub fn set_peek_saved(&mut self, k: usize, v: i64) {
let n = self.save.stack.len();
if let Some(SaveEntry::Value(x)) = self.save.stack.get_mut(n.wrapping_sub(1 + k)) {
*x = v;
}
}
pub fn new_param_glue(&mut self, sub: u16) -> NodeRef {
let g = self.eqtb.glue(glue_param_of_subtype(sub));
self.new_node(
id::GLUE,
sub,
NodeData::Glue {
spec: g,
leader: NodeRef::NULL,
},
)
}
pub fn new_glue_node(&mut self, spec: Glue, sub: u16) -> NodeRef {
self.new_node(
id::GLUE,
sub,
NodeData::Glue {
spec,
leader: NodeRef::NULL,
},
)
}
pub fn new_kern_node(&mut self, w: i32, sub: u16) -> NodeRef {
self.new_node(
id::KERN,
sub,
NodeData::Kern {
kern: w,
expansion_factor: 0,
},
)
}
pub fn new_penalty_node(&mut self, m: i32) -> NodeRef {
self.new_node(id::PENALTY, 0, NodeData::Penalty { penalty: m })
}
pub fn new_null_box_node(&mut self, hlist: bool) -> NodeRef {
let p = self.nodes.new_null_box(hlist);
self.give_new_attr(p);
p
}
pub fn typeset_command(&mut self) {
use ModeKind as K;
let kind = self.mode.abs_kind();
let cmd = self.cur_cmd;
match (kind, cmd) {
(
K::Vertical,
Cmd::Letter
| Cmd::OtherChar
| Cmd::CharNum
| Cmd::CharGiven
| Cmd::MathShift
| Cmd::MathShiftCs
| Cmd::UnHbox
| Cmd::Vrule
| Cmd::Accent
| Cmd::Discretionary
| Cmd::Hskip
| Cmd::Valign
| Cmd::ExSpace
| Cmd::Boundary
| Cmd::CharGhost
| Cmd::MathCharNum
| Cmd::MathGiven
| Cmd::XmathGiven,
) => {
self.back_input();
self.new_graf(true);
}
(K::Horizontal, Cmd::MathCharNum | Cmd::MathGiven | Cmd::XmathGiven) => {
self.math_char_in_text();
}
(K::Vertical, Cmd::StartPar) => {
let c = self.cur_chr;
self.new_graf(c != chr::NOINDENT);
}
(K::Horizontal | K::Math, Cmd::StartPar) => self.indent_in_hmode(),
(K::Vertical, Cmd::Hrule) | (K::Horizontal | K::Math, Cmd::Vrule) => {
let r = self.scan_rule_spec();
self.tail_append(r);
if kind == K::Vertical {
self.cur_list_mut().prev_depth = IGNORE_DEPTH;
} else if kind == K::Horizontal {
self.cur_list_mut().space_factor = 1000;
}
}
(K::Vertical, Cmd::Nohrule) | (K::Horizontal | K::Math, Cmd::Novrule) => {
let r = self.scan_rule_spec();
self.nodes.set_subtype(r, 3);
self.tail_append(r);
if kind == K::Vertical {
self.cur_list_mut().prev_depth = IGNORE_DEPTH;
} else if kind == K::Horizontal {
self.cur_list_mut().space_factor = 1000;
}
}
(K::Vertical, Cmd::Vskip)
| (K::Horizontal | K::Math, Cmd::Hskip)
| (K::Math, Cmd::Mskip) => self.append_glue(),
(_, Cmd::Kern) | (K::Math, Cmd::Mkern) => self.append_kern(),
(K::Vertical, Cmd::Hmove) | (K::Horizontal | K::Math, Cmd::Vmove) => {
let t = self.cur_chr;
self.scan_normal_dimen();
let v = self.cur_val;
self.scan_box(BoxContext::Shift(if t == 0 { v } else { -v }));
}
(_, Cmd::LeaderShip) => {
let c = self.cur_chr;
if c == chr::SHIPOUT {
self.scan_box(BoxContext::Shipout);
} else if c >= glue_sub::A_LEADERS as u32 {
self.scan_box(BoxContext::Leaders(c as u16));
} else {
self.scan_box(BoxContext::Shipout);
}
}
(_, Cmd::MakeBox) => self.begin_box(BoxContext::Shift(0)),
(K::Vertical, Cmd::ParEnd) => {
self.normal_paragraph();
if self.mode.is_privileged() {
self.build_page();
}
}
(K::Horizontal, Cmd::ParEnd) => {
if self.input.align_state < 0 {
self.off_save();
}
self.end_graf();
if self.mode == Mode::Vertical {
self.build_page();
}
}
(K::Horizontal, Cmd::Stop | Cmd::Vskip | Cmd::Hrule | Cmd::UnVbox | Cmd::Halign) => {
self.head_for_vmode()
}
(_, Cmd::Insert) | (K::Horizontal | K::Math, Cmd::Vadjust) => {
self.begin_insert_or_adjust()
}
(K::Vertical, Cmd::Vadjust) => self.report_illegal_case(),
(_, Cmd::Mark) => self.make_mark(),
(_, Cmd::BreakPenalty) => self.append_penalty(),
(_, Cmd::RemoveItem) => self.delete_last(),
(K::Vertical, Cmd::UnVbox) | (K::Horizontal | K::Math, Cmd::UnHbox) => self.unpackage(),
(K::Horizontal, Cmd::ItalCorr) => self.append_italic_correction(),
(K::Math, Cmd::ItalCorr) => {
let k = self.new_kern_node(0, kern_sub::FONT);
self.tail_append(k);
}
(K::Vertical, Cmd::ItalCorr) => self.report_illegal_case(),
(K::Horizontal | K::Math, Cmd::Discretionary) => self.append_discretionary(),
(K::Horizontal, Cmd::Accent) => self.make_accent(),
(K::Horizontal, Cmd::Boundary) => self.append_boundary(),
(K::Horizontal, Cmd::CharGhost) => self.append_ghost(),
(_, Cmd::CarRet | Cmd::TabMark) => self.align_error(),
(_, Cmd::NoAlign) => self.no_align_error(),
(_, Cmd::Omit) => self.omit_error(),
(K::Vertical, Cmd::Halign) | (K::Horizontal, Cmd::Valign) => self.init_align(),
(K::Math, Cmd::Halign) => {
if self.privileged() {
if self.save.cur_group == GroupCode::MathShift {
self.init_align();
} else {
self.off_save();
}
}
}
(K::Vertical | K::Horizontal, Cmd::Endv) => self.do_endv(),
(_, Cmd::EndCsName) => self.cs_error(),
(_, Cmd::AssignLocalBox) => self.begin_local_box(),
(K::Horizontal, Cmd::MathShift | Cmd::MathShiftCs) => self.init_math(),
(K::Math, _) => self.math_command(),
(
K::Vertical | K::Horizontal,
Cmd::SupMark
| Cmd::SubMark
| Cmd::MathComp
| Cmd::DelimNum
| Cmd::LeftRight
| Cmd::Above
| Cmd::Radical
| Cmd::MathStyle
| Cmd::MathChoice
| Cmd::Vcenter
| Cmd::NonScript
| Cmd::Mkern
| Cmd::LimitSwitch
| Cmd::Mskip
| Cmd::MathAccent
| Cmd::SuperSubScript
| Cmd::NoSuperSubScript,
) => self.insert_dollar_sign(),
(K::Vertical | K::Horizontal, Cmd::EqNo) => self.report_illegal_case(),
(_, Cmd::Option) => {
self.scan_int();
self.scan_optional_equals();
self.scan_int();
}
(_, Cmd::Node) => {
self.scan_int();
}
_ => {
let (c, r) = (self.cur_cmd, self.cur_chr);
let s = self.print_to_string(|e| e.print_cmd_chr(c, r));
self.not_implemented(&String::from_utf8_lossy(&s));
}
}
}
pub fn append_glue(&mut self) {
let s = self.cur_chr;
let g = match s {
chr::FIL => Glue {
stretch: retex_arith::UNITY,
stretch_order: FIL,
..Glue::ZERO
},
chr::FILL => Glue {
stretch: retex_arith::UNITY,
stretch_order: FILL,
..Glue::ZERO
},
chr::SS => Glue {
stretch: retex_arith::UNITY,
stretch_order: FIL,
shrink: retex_arith::UNITY,
shrink_order: FIL,
..Glue::ZERO
},
chr::FIL_NEG => Glue {
stretch: -retex_arith::UNITY,
stretch_order: FIL,
..Glue::ZERO
},
chr::SKIP => {
self.scan_glue(ValLevel::Glue);
self.cur_glue
}
_ => {
self.scan_glue(ValLevel::Mu);
self.cur_glue
}
};
let sub = if s == chr::MSKIP {
glue_sub::MU_GLUE
} else {
glue_sub::USER
};
let p = self.new_glue_node(g, sub);
self.tail_append(p);
}
pub fn append_kern(&mut self) {
let s = self.cur_chr as u16;
let mu = s == glue_sub::MU_GLUE;
self.scan_dimen(mu, false, false);
let v = self.cur_val;
let p = self.new_kern_node(
v,
if mu {
glue_sub::MU_GLUE
} else {
kern_sub::USER
},
);
self.tail_append(p);
}
pub fn scan_rule_spec(&mut self) -> NodeRef {
let vertical_rule = matches!(self.cur_cmd, Cmd::Vrule | Cmd::Novrule);
let (mut w, mut h, mut d) = if vertical_rule {
(26214, NULL_FLAG, NULL_FLAG)
} else {
(NULL_FLAG, 26214, 0)
};
let (mut left, mut right) = (0, 0);
let sync = self.nodes.sync.as_ref().map(|s| s.cur);
loop {
if self.scan_keyword("width") {
self.scan_normal_dimen();
w = self.cur_val;
continue;
}
if self.scan_keyword("height") {
self.scan_normal_dimen();
h = self.cur_val;
continue;
}
if self.scan_keyword("depth") {
self.scan_normal_dimen();
d = self.cur_val;
continue;
}
if self.scan_keyword("left") {
self.scan_normal_dimen();
left = self.cur_val;
continue;
}
if self.scan_keyword("right") {
self.scan_normal_dimen();
right = self.cur_val;
continue;
}
break;
}
let p = self.new_node(
id::RULE,
0,
NodeData::Rule {
width: w,
depth: d,
height: h,
dir: 0,
index: 0,
left,
right,
},
);
if let Some(f) = sync {
self.nodes.set_sync_fields(p, f);
}
p
}
pub fn scan_spec(&mut self, c: GroupCode, ctx: Option<i64>) {
let mut dir: i64 = -1;
let mut attrs: Vec<(u32, i32)> = Vec::new();
loop {
if self.scan_keyword("attr") {
self.scan_register_num();
let n = self.cur_val as u32;
self.scan_optional_equals();
self.scan_int();
attrs.retain(|(k, _)| *k != n);
attrs.push((n, self.cur_val));
continue;
}
if dir < 0 && self.scan_keyword("dir") {
dir = i64::from(self.scan_direction_spec());
continue;
}
if dir < 0 && self.scan_keyword("bdir") {
self.scan_int();
dir = if (0..=3).contains(&self.cur_val) {
i64::from(self.cur_val)
} else {
0
};
continue;
}
break;
}
let is_box = matches!(
c,
GroupCode::Hbox | GroupCode::AdjustedHbox | GroupCode::Vbox | GroupCode::Vtop
);
if dir < 0 && is_box {
dir = i64::from(self.default_box_dir());
}
let spec = if self.scan_keyword("to") {
self.scan_normal_dimen();
PackSpec::Exactly(self.cur_val)
} else if self.scan_keyword("spread") {
self.scan_normal_dimen();
PackSpec::Additional(self.cur_val)
} else {
PackSpec::NATURAL
};
if let Some(v) = ctx {
self.push_saved(v);
}
let (code, v) = match spec {
PackSpec::Exactly(v) => (0, v),
PackSpec::Additional(v) => (1, v),
};
self.push_saved(code);
self.push_saved(i64::from(v));
self.push_saved(dir);
if is_box {
let mut a = self.cur_attr();
if !attrs.is_empty() {
let mut v: Vec<(u32, i32)> = a.as_deref().unwrap_or(&[]).to_vec();
for (n, x) in attrs {
v.retain(|(k, _)| *k != n);
if x != crate::eqtb::UNUSED_ATTRIBUTE {
v.push((n, x));
}
}
v.sort_unstable();
a = if v.is_empty() {
None
} else {
Some(std::rc::Rc::from(v))
};
}
self.push_saved(self.ts.box_attrs.len() as i64);
self.ts.box_attrs.push(a);
}
self.new_save_level(c);
self.begin_box_dirs(c, dir as i32);
self.scan_left_brace();
}
pub fn pop_spec_pub(&mut self) -> (PackSpec, i64) {
self.pop_spec()
}
fn pop_spec(&mut self) -> (PackSpec, i64) {
let dir = self.pop_saved();
let v = self.pop_saved() as i32;
let code = self.pop_saved();
(
if code == 0 {
PackSpec::Exactly(v)
} else {
PackSpec::Additional(v)
},
dir,
)
}
pub fn scan_box(&mut self, ctx: BoxContext) {
self.get_nonblank_nonrelax_noncall();
if self.cur_cmd == Cmd::MakeBox {
self.begin_box(ctx);
} else if matches!(ctx, BoxContext::Leaders(_))
&& matches!(
self.cur_cmd,
Cmd::Hrule | Cmd::Vrule | Cmd::Nohrule | Cmd::Novrule
)
{
let r = self.scan_rule_spec();
self.box_end(ctx, r);
} else {
self.print_err("A <box> was supposed to be here");
self.help(&[
"I was expecting to see \\hbox or \\vbox or \\copy or \\box or",
"something like that. So you might find something missing in",
"your output. But keep trying; you can fix this later.",
]);
self.back_error();
}
}
pub fn begin_box(&mut self, ctx: BoxContext) {
let cur_box: NodeRef;
match self.cur_chr {
chr::BOX => {
self.scan_register_num();
let n = self.cur_val as usize;
cur_box = self.eqtb.boxes[n].val;
self.eqtb.boxes[n].val = NodeRef::NULL;
}
chr::COPY => {
self.scan_register_num();
let b = self.eqtb.boxes[self.cur_val as usize].val;
cur_box = self.copy_node_list(b);
}
chr::LAST_BOX => {
cur_box = self.take_last_box();
}
chr::VSPLIT => {
self.scan_register_num();
let n = self.cur_val as u32;
if !self.scan_keyword("to") {
self.print_err("Missing `to' inserted");
self.help(&[
"I'm working on `\\vsplit<box number> to <dimen>';",
"will look for the <dimen> next.",
]);
self.error();
}
self.scan_normal_dimen();
let h = self.cur_val;
cur_box = self.vsplit(n, h);
}
c => {
let k = match c {
chr::HBOX | chr::HPACK => ModeKind::Horizontal,
_ => ModeKind::Vertical,
};
let ctxv = ctx_to_i64(ctx);
self.push_saved(i64::from(c));
if k == ModeKind::Horizontal {
if matches!(ctx, BoxContext::Shift(_))
&& self.mode.abs_kind() == ModeKind::Vertical
{
self.scan_spec(GroupCode::AdjustedHbox, Some(ctxv));
} else {
self.scan_spec(GroupCode::Hbox, Some(ctxv));
}
} else {
if c == chr::VBOX || c == chr::VPACK {
self.scan_spec(GroupCode::Vbox, Some(ctxv));
} else {
self.scan_spec(GroupCode::Vtop, Some(ctxv));
}
self.normal_paragraph();
}
if k == ModeKind::Vertical {
self.push_nest(Mode::InternalVertical);
self.cur_list_mut().prev_depth = IGNORE_DEPTH;
let t = self
.eqtb
.toks_param(crate::primitives_gen::assign_toks::EVERYVBOX)
.clone();
if !t.is_empty() {
self.begin_token_list(t, TokKind::EveryVboxText);
}
} else {
self.push_nest(Mode::RestrictedHorizontal);
self.cur_list_mut().space_factor = 1000;
let t = self
.eqtb
.toks_param(crate::primitives_gen::assign_toks::EVERYHBOX)
.clone();
if !t.is_empty() {
self.begin_token_list(t, TokKind::EveryHboxText);
}
}
return;
}
}
self.box_end(ctx, cur_box);
}
pub fn copy_node_list(&mut self, p: NodeRef) -> NodeRef {
if p.is_null() {
return p;
}
self.nodes.copy_node(p)
}
fn take_last_box(&mut self) -> NodeRef {
if self.mode.abs_kind() == ModeKind::Math {
self.you_cant();
self.help(&["Sorry; this \\lastbox will be void."]);
self.error();
return NodeRef::NULL;
}
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
if self.mode == Mode::Vertical && head == tail {
self.you_cant();
self.help(&[
"Sorry...I usually can't take things from the current page.",
"This \\lastbox will therefore be void.",
]);
self.error();
return NodeRef::NULL;
}
if tail == head {
return NodeRef::NULL;
}
let t = self.nodes.id(tail);
if t != id::HLIST && t != id::VLIST {
return NodeRef::NULL;
}
let p = self.nodes.prev(tail);
let p = if p.is_null() { self.find_prev(tail) } else { p };
self.nodes.set_next(p, NodeRef::NULL);
self.nodes.set_prev(tail, NodeRef::NULL);
self.cur_list_mut().tail = p;
if let Some(b) = self.nodes.box_data_mut(tail) {
b.shift = 0;
}
tail
}
fn find_prev(&self, q: NodeRef) -> NodeRef {
let mut p = self.cur_list().head;
while !p.is_null() {
let n = self.nodes.next(p);
if n == q {
return p;
}
p = n;
}
self.cur_list().head
}
pub fn box_end(&mut self, ctx: BoxContext, cur_box: NodeRef) {
match ctx {
BoxContext::Shift(s) => {
if cur_box.is_null() {
return;
}
if let Some(b) = self.nodes.box_data_mut(cur_box) {
b.shift = s;
}
match self.mode.abs_kind() {
ModeKind::Vertical => {
self.append_box_with_adjustments(cur_box);
if self.mode.is_privileged() {
self.build_page();
}
}
ModeKind::Horizontal => {
self.cur_list_mut().space_factor = 1000;
self.tail_append(cur_box);
}
_ => {
self.math_box_end(cur_box);
}
}
}
BoxContext::SetBox(n) => self.set_box_register(n, false, cur_box),
BoxContext::GlobalSetBox(n) => self.set_box_register(n, true, cur_box),
BoxContext::Shipout => {
if !cur_box.is_null() {
self.ship_out(cur_box);
}
}
BoxContext::Leaders(sub) => {
if cur_box.is_null() {
return;
}
self.get_nonblank_nonrelax_noncall();
let v = self.mode.abs_kind() == ModeKind::Vertical;
if (self.cur_cmd == Cmd::Hskip && !v) || (self.cur_cmd == Cmd::Vskip && v) {
self.append_glue();
let t = self.cur_list().tail;
self.nodes.set_subtype(t, sub);
if let NodeData::Glue { leader, .. } = self.nodes.data_mut(t) {
*leader = cur_box;
}
} else {
self.print_err("Leaders not followed by proper glue");
self.help(&[
"You should say `\\leaders <box or rule><hskip or vskip>'.",
"I found the <box or rule>, but there's no suitable",
"<hskip or vskip>, so I'm ignoring these leaders.",
]);
self.back_error();
self.nodes.flush_list(cur_box);
}
}
}
}
pub fn start_adjust_lists(&mut self) {
if self.ts.adjust_head.is_null() {
self.ts.adjust_head = self.nodes.new_temp();
self.ts.pre_adjust_head = self.nodes.new_temp();
}
self.nodes.set_next(self.ts.adjust_head, NodeRef::NULL);
self.nodes.set_next(self.ts.pre_adjust_head, NodeRef::NULL);
self.ts.adjust_tail = Some(self.ts.adjust_head);
self.ts.pre_adjust_tail = Some(self.ts.pre_adjust_head);
}
fn append_adjust_list(&mut self, head: NodeRef) {
let first = self.nodes.next(head);
if first.is_null() {
return;
}
let tail = self.cur_list().tail;
self.nodes.link(tail, first);
let t = self.nodes.tail_of(first);
self.cur_list_mut().tail = t;
self.nodes.set_next(head, NodeRef::NULL);
}
fn append_box_with_adjustments(&mut self, b: NodeRef) {
let active = self.ts.adjust_tail.is_some();
if active {
let h = self.ts.pre_adjust_head;
self.append_adjust_list(h);
}
self.append_to_vlist(b);
if active {
let h = self.ts.adjust_head;
self.append_adjust_list(h);
}
self.ts.adjust_tail = None;
self.ts.pre_adjust_tail = None;
}
pub fn set_box_register(&mut self, n: u32, global: bool, b: NodeRef) {
let old = self.eqtb.boxes[n as usize].val;
let old_level = self.eqtb.boxes[n as usize].level;
let saved = !global && old_level != self.save.cur_level && self.save.cur_level > 1;
self.define(global, Loc::Box(n), Val::Node(b));
if !saved && !old.is_null() && old != b {
self.nodes.flush_list(old);
}
}
fn package(&mut self, vtop: bool) {
let d = self.eqtb.dimen(dp::BOXMAXDEPTH);
let group = self.save.cur_group;
self.unsave();
self.end_box_dirs();
let ai = self.pop_saved() as usize;
let attr = self.ts.box_attrs.get(ai).cloned().flatten();
self.ts.box_attrs.truncate(ai);
let (spec, dir) = self.pop_spec();
let ctx = ctx_from_i64(self.pop_saved());
let kind = self.pop_saved() as u32;
let head = self.cur_list().head;
let first = self.nodes.next(head);
self.nodes.set_next(head, NodeRef::NULL);
let cur_box = if self.mode == Mode::RestrictedHorizontal {
let first = if kind == chr::HPACK {
first
} else {
let first = self.node_process_list(first, crate::nodeproc::Context::Hbox);
let g = if group == GroupCode::AdjustedHbox {
"adjusted_hbox"
} else {
"hbox"
};
self.filter_list("hpack_filter", first, g)
};
self.pack_dir = dir as i8;
self.hpack(first, spec)
} else {
let first = if kind == chr::VPACK || kind == chr::TPACK {
first
} else {
self.filter_list("vpack_filter", first, if vtop { "vtop" } else { "vbox" })
};
self.pack_dir = dir as i8;
let b = self.vpackage(first, spec, d);
if vtop {
let p = self.nodes.list_ptr(b);
let h = if !p.is_null() && self.nodes.id(p) <= id::RULE {
self.node_height(p)
} else {
0
};
if let Some(bd) = self.nodes.box_data_mut(b) {
bd.depth = bd.depth - h + bd.height;
bd.height = h;
}
}
b
};
if self.nodes.id(cur_box) == id::HLIST {
self.nodes.set_subtype(cur_box, 2);
}
if self.eqtb.attrs_in_use || attr.is_some() {
self.nodes.set_attr(cur_box, attr);
}
self.pop_nest();
self.box_end(ctx, cur_box);
}
pub fn node_height(&self, p: NodeRef) -> i32 {
match self.nodes.data(p) {
NodeData::Box(b) => b.height,
NodeData::Unset { b, .. } => b.height,
NodeData::Rule { height, .. } => *height,
_ => 0,
}
}
pub fn node_depth(&self, p: NodeRef) -> i32 {
match self.nodes.data(p) {
NodeData::Box(b) => b.depth,
NodeData::Unset { b, .. } => b.depth,
NodeData::Rule { depth, .. } => *depth,
_ => 0,
}
}
pub fn new_local_par(&mut self) -> NodeRef {
let lb = self.eqtb.local_box[0].val;
let rb = self.eqtb.local_box[1].val;
let box_left = self.copy_node_list(lb);
let box_right = self.copy_node_list(rb);
let box_left_width = if lb.is_null() {
0
} else {
self.nodes.width(lb)
};
let box_right_width = if rb.is_null() {
0
} else {
self.nodes.width(rb)
};
let dir = self.dir_param(crate::primitives_gen::assign_dir::PARDIR) as i8;
self.new_node(
id::LOCAL_PAR,
0,
NodeData::LocalPar {
pen_inter: self.eqtb.int(ip::LOCALINTERLINEPENALTY),
pen_broken: self.eqtb.int(ip::LOCALBROKENPENALTY),
dir,
box_left,
box_left_width,
box_right,
box_right_width,
},
)
}
pub fn new_graf(&mut self, indented: bool) {
self.cur_list_mut().prev_graf = 0;
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
if self.mode == Mode::Vertical || head != tail {
let g = self.new_param_glue(glue_sub::PAR_SKIP);
self.tail_append(g);
}
self.push_nest(Mode::Horizontal);
self.cur_list_mut().space_factor = 1000;
let l = self.cur_lang() as i32;
self.cur_list_mut().clang = l;
let lp = self.new_local_par();
self.tail_append(lp);
self.par_text_dir();
if indented {
let b = self.new_null_box_node(true);
let w = self.eqtb.dimen(dp::PARINDENT);
let dir = self.dir_param(crate::primitives_gen::assign_dir::PARDIR) as i8;
if let Some(bd) = self.nodes.box_data_mut(b) {
bd.width = w;
bd.dir = dir;
}
self.nodes.set_subtype(b, 3);
self.tail_append(b);
}
let t = self
.eqtb
.toks_param(crate::primitives_gen::assign_toks::EVERYPAR)
.clone();
if !t.is_empty() {
self.begin_token_list(t, TokKind::EveryParText);
}
if self.nest.len() == 2 {
self.build_page();
}
}
fn indent_in_hmode(&mut self) {
if self.cur_chr == chr::INDENT {
let b = self.new_null_box_node(true);
let w = self.eqtb.dimen(dp::PARINDENT);
if let Some(bd) = self.nodes.box_data_mut(b) {
bd.width = w;
}
if self.mode.abs_kind() == ModeKind::Horizontal {
self.cur_list_mut().space_factor = 1000;
self.tail_append(b);
} else {
self.math_box_end(b);
}
}
}
fn head_for_vmode(&mut self) {
if self.mode.is_inner() {
if self.cur_cmd != Cmd::Hrule {
self.off_save();
} else {
self.print_err("You can't use `");
self.print_esc_str("hrule");
self.printer.print_str("' here except with leaders");
self.help(&[
"To put a horizontal rule in an hbox or an alignment,",
"you should use \\leaders or \\hrulefill (see The TeXbook).",
]);
self.error();
}
} else {
self.back_input();
self.cur_tok = Token::cs(self.par_token_cs);
self.back_input();
if let Some(crate::input::Level::Tokens(t)) = self.input.stack.last_mut() {
t.kind = TokKind::Inserted;
}
}
}
pub fn end_graf(&mut self) {
if self.mode == Mode::Horizontal {
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
let only_local_par = self.nodes.next(head) == tail
&& matches!(self.nodes.data(tail), NodeData::LocalPar { .. });
if head == tail || only_local_par {
self.nodes.flush_list(tail);
self.pop_nest();
} else {
let wp = self.eqtb.int(ip::WIDOWPENALTY);
self.line_break(wp);
}
self.normal_paragraph();
self.err.error_count = 0;
}
}
pub fn normal_paragraph(&mut self) {
use crate::primitives_gen::set_etex_shape as es;
if self.eqtb.int(ip::LOOSENESS) != 0 {
self.eq_define(Loc::IntPar(ip::LOOSENESS), Val::Int(0));
}
if self.eqtb.dimen(dp::HANGINDENT) != 0 {
self.eq_define(Loc::DimenPar(dp::HANGINDENT), Val::Int(0));
}
if self.eqtb.int(ip::HANGAFTER) != 1 {
self.eq_define(Loc::IntPar(ip::HANGAFTER), Val::Int(1));
}
if self.eqtb.par_shape.val.is_some() {
self.eq_define(Loc::ParShape, Val::Shape(None));
}
if self.eqtb.etex_shape[es::INTERLINEPENALTIES as usize]
.val
.is_some()
{
self.eq_define(Loc::EtexShape(es::INTERLINEPENALTIES), Val::Shape(None));
}
}
fn begin_insert_or_adjust(&mut self) {
let v: i64 = if self.cur_cmd == Cmd::Vadjust {
let pre = self.scan_keyword("pre");
if pre { -2 } else { -1 }
} else {
self.scan_register_num();
let mut n = self.cur_val;
if n == self.output_box_num() {
self.print_err("You can't ");
self.print_esc_str("insert");
self.printer.print_int(i64::from(n));
self.help(&["I'm changing to \\insert0; box \\outputbox is special."]);
self.error();
n = 0;
}
i64::from(n)
};
self.push_saved(v);
self.new_save_level(GroupCode::Insert);
self.scan_left_brace();
self.normal_paragraph();
self.push_nest(Mode::InternalVertical);
self.cur_list_mut().prev_depth = IGNORE_DEPTH;
}
pub fn output_box_num(&self) -> i32 {
let n = self.eqtb.int(ip::OUTPUTBOX);
if (0..65536).contains(&n) { n } else { 255 }
}
pub fn par_token_before_group_end(&mut self, level: i32) -> bool {
if self.mode != Mode::Horizontal || self.eqtb.int(ip::PARTOKENCONTEXT) < level {
return false;
}
self.back_input();
self.cur_tok = Token::cs(self.par_token_cs);
self.back_input();
true
}
fn finish_insert_group(&mut self) {
if self.par_token_before_group_end(2) {
return;
}
self.end_graf();
let q = self.eqtb.glue(gp::SPLITTOPSKIP);
let d = self.eqtb.dimen(dp::SPLITMAXDEPTH);
let f = self.eqtb.int(ip::FLOATINGPENALTY);
self.unsave();
let n = self.pop_saved();
let head = self.cur_list().head;
let first = self.nodes.next(head);
self.nodes.set_next(head, NodeRef::NULL);
let p = self.vpack(first, PackSpec::NATURAL);
self.pop_nest();
let list = self.nodes.list_ptr(p);
self.nodes.set_list_ptr(p, NodeRef::NULL);
let (h, dd) = match self.nodes.box_data(p) {
Some(b) => (b.height, b.depth),
None => (0, 0),
};
if n >= 0 {
let node = self.new_node(
id::INS,
n as u16,
NodeData::Ins {
cost: f,
depth: d,
height: h + dd,
spec: q,
head: list,
},
);
self.tail_append(node);
} else {
let node = self.new_node(
id::ADJUST,
if n == -2 { 1 } else { 0 },
NodeData::Adjust { head: list },
);
self.tail_append(node);
}
self.nodes.free(p);
if self.nest.len() == 1 {
self.build_page();
}
}
fn make_mark(&mut self) {
let c: u32 = if self.cur_chr == 0 {
0
} else if self.cur_chr == 1 {
self.scan_register_num();
let n = self.cur_val as u32;
self.marks.classes.remove(&n);
return;
} else {
self.scan_register_num();
self.cur_val as u32
};
let toks = self.scan_toks_expanded();
let p = self.new_node(
id::MARK,
0,
NodeData::Mark {
class: c,
mark: std::rc::Rc::from(toks),
},
);
self.tail_append(p);
}
fn append_penalty(&mut self) {
self.scan_int();
let p = self.new_penalty_node(self.cur_val);
self.tail_append(p);
if self.mode == Mode::Vertical {
self.build_page();
}
}
fn delete_last(&mut self) {
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
let want = self.cur_chr as u8;
if self.mode == Mode::Vertical && tail == head {
if want != id::GLUE || self.page.last_glue.is_some() {
self.you_cant();
let l0 = if want == id::KERN {
"Try `I\\kern-\\lastkern' instead."
} else if want != id::GLUE {
"Perhaps you can make the output routine do it."
} else {
"Try `I\\vskip-\\lastskip' instead."
};
self.help(&[
"Sorry...I usually can't take things from the current page.",
l0,
]);
self.error();
}
return;
}
if tail == head || self.nodes.id(tail) != want {
return;
}
let p = self.find_prev(tail);
self.nodes.set_next(p, NodeRef::NULL);
self.nodes.flush_list(tail);
self.cur_list_mut().tail = p;
}
fn unpackage(&mut self) {
let c = self.cur_chr;
if c > chr::COPY {
let k = c as usize;
let p = self.ts.disc_ptr[k];
self.ts.disc_ptr[k] = NodeRef::NULL;
if !p.is_null() {
let tail = self.cur_list().tail;
self.nodes.link(tail, p);
let t = self.nodes.tail_of(p);
self.cur_list_mut().tail = t;
}
return;
}
self.scan_register_num();
let n = self.cur_val as usize;
let p = self.eqtb.boxes[n].val;
if p.is_null() {
return;
}
let t = self.nodes.id(p);
let k = self.mode.abs_kind();
if k == ModeKind::Math
|| (k == ModeKind::Vertical && t != id::VLIST)
|| (k == ModeKind::Horizontal && t != id::HLIST)
{
self.print_err("Incompatible list can't be unboxed");
self.help(&[
"Sorry, Pandora. (You sneaky devil.)",
"I refuse to unbox an \\hbox in vertical mode or vice versa.",
"And I can't open any boxes in math mode.",
]);
self.error();
return;
}
let list = if c == chr::COPY {
let l = self.nodes.list_ptr(p);
self.nodes.copy_list(l)
} else {
let l = self.nodes.list_ptr(p);
self.nodes.set_list_ptr(p, NodeRef::NULL);
self.eqtb.boxes[n].val = NodeRef::NULL;
self.nodes.free(p);
l
};
let list = if t == id::HLIST {
self.strip_margin_kerns(list)
} else {
list
};
if !list.is_null() {
let tail = self.cur_list().tail;
self.nodes.link(tail, list);
let t = self.nodes.tail_of(list);
self.cur_list_mut().tail = t;
}
}
fn append_italic_correction(&mut self) {
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
if tail == head {
return;
}
let Some(g) = self.nodes.glyph(tail) else {
return;
};
let (f, c) = (g.font, g.chr);
let ic = self.font_char_dimen(f, c, 3);
let k = self.new_kern_node(ic, kern_sub::ITALIC);
self.tail_append(k);
}
fn append_discretionary(&mut self) {
let c = self.cur_chr;
if c == 1 {
let pen = self.eqtb.int(ip::EXHYPHENPENALTY);
let d = self.new_node(
id::DISC,
1,
NodeData::Disc {
pre: NodeRef::NULL,
post: NodeRef::NULL,
replace: NodeRef::NULL,
penalty: pen,
},
);
let lang = self.cur_lang();
let (pre_c, post_c) = {
let l = self.lang_params(lang);
(l.pre_hyphen_char, l.post_hyphen_char)
};
let f = self.eqtb.cur_font.val;
let pre = if pre_c > 0 {
self.new_char_node(f, pre_c as u32)
} else {
NodeRef::NULL
};
let post = if post_c > 0 {
self.new_char_node(f, post_c as u32)
} else {
NodeRef::NULL
};
self.nodes.set_disc_lists(d, (pre, post, NodeRef::NULL));
self.tail_append(d);
return;
}
let mut pen = self.eqtb.int(ip::HYPHENPENALTY);
if self.scan_keyword("penalty") {
self.scan_int();
pen = self.cur_val;
}
let d = self.new_node(
id::DISC,
if c == 2 { 2 } else { 0 },
NodeData::Disc {
pre: NodeRef::NULL,
post: NodeRef::NULL,
replace: NodeRef::NULL,
penalty: pen,
},
);
self.tail_append(d);
self.push_saved(0);
self.new_save_level(GroupCode::Disc);
self.scan_left_brace();
self.push_nest(Mode::RestrictedHorizontal);
self.cur_list_mut().space_factor = 1000;
}
fn build_discretionary(&mut self) {
self.unsave();
let head = self.cur_list().head;
let mut q = head;
let mut p = self.nodes.next(q);
while !p.is_null() {
let t = self.nodes.id(p);
if !matches!(t, id::GLYPH | id::KERN | id::HLIST | id::VLIST | id::RULE) {
self.print_err("Improper discretionary list");
self.help(&["Discretionary lists must contain only boxes and kerns."]);
self.error();
self.begin_diagnostic();
self.printer
.print_nl("The following discretionary sublist has been deleted:");
self.show_box(p);
self.end_diagnostic(true);
self.nodes.flush_list(p);
self.nodes.set_next(q, NodeRef::NULL);
break;
}
q = p;
p = self.nodes.next(q);
}
let list = self.nodes.next(head);
self.nodes.set_next(head, NodeRef::NULL);
if !list.is_null() {
self.nodes.set_prev(list, NodeRef::NULL);
}
self.pop_nest();
let k = self.pop_saved();
let d = self.cur_list().tail;
let (mut pre, mut post, mut rep) = self.nodes.disc_lists(d);
match k {
0 => pre = list,
1 => post = list,
_ => {
if !list.is_null() && self.mode.abs_kind() == ModeKind::Math {
self.print_err("Illegal math ");
self.print_esc_str("discretionary");
self.help(&[
"Sorry: The third part of a discretionary break must be",
"empty, in math formulas. I had to delete your third part.",
]);
self.nodes.flush_list(list);
self.error();
} else {
rep = list;
}
self.nodes.set_disc_lists(d, (pre, post, rep));
return;
}
}
self.nodes.set_disc_lists(d, (pre, post, rep));
self.push_saved(k + 1);
self.new_save_level(GroupCode::Disc);
self.scan_left_brace();
self.push_nest(Mode::RestrictedHorizontal);
self.cur_list_mut().space_factor = 1000;
}
fn make_accent(&mut self) {
self.scan_char_num();
let f = self.eqtb.cur_font.val;
let p = self.new_char_node(f, self.cur_val as u32);
let x = self.x_height(f);
let s = f64::from(self.font(f).params.get(1).copied().unwrap_or(0)) / 65536.0;
let a = self.char_width(f, self.cur_val as u32);
self.do_assignments();
let mut q = NodeRef::NULL;
let f2 = self.eqtb.cur_font.val;
if matches!(self.cur_cmd, Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven) {
q = self.new_char_node(f2, self.cur_chr);
} else if self.cur_cmd == Cmd::CharNum {
self.scan_char_num();
q = self.new_char_node(f2, self.cur_val as u32);
} else {
self.back_input();
}
let mut p = p;
if !q.is_null() {
let t = f64::from(self.font(f2).params.get(1).copied().unwrap_or(0)) / 65536.0;
let qc = self.nodes.glyph(q).map_or(0, |g| g.chr);
let w = self.char_width(f2, qc);
let h = self.font_char_dimen(f2, qc, 1);
if h != x {
p = self.hpack(p, PackSpec::NATURAL);
if let Some(b) = self.nodes.box_data_mut(p) {
b.shift = x - h;
}
}
let delta =
((f64::from(w - a)) / 2.0 + f64::from(h) * t - f64::from(x) * s).round() as i32;
let r = self.new_kern_node(delta, kern_sub::ACCENT);
self.tail_append(r);
self.tail_append(p);
let k = self.new_kern_node(-a - delta, kern_sub::ACCENT);
self.tail_append(k);
p = q;
}
self.tail_append(p);
self.cur_list_mut().space_factor = 1000;
}
#[inline]
pub fn char_width(&self, f: u32, c: u32) -> i32 {
self.font(f).chars.get(&c).map_or(0, |m| m.width)
}
pub fn do_assignments(&mut self) {
loop {
self.get_nonblank_nonrelax_noncall();
if self.cur_cmd <= Cmd::MAX_NON_PREFIXED {
return;
}
self.err.set_box_allowed = false;
self.prefixed_command();
self.err.set_box_allowed = true;
}
}
pub fn append_boundary(&mut self) {
let c = self.cur_chr;
let v = if c == 1 || c == 2 {
self.scan_int();
self.cur_val
} else {
0
};
let p = self.new_node(id::BOUNDARY, c as u16, NodeData::Boundary { value: v });
self.tail_append(p);
}
fn append_ghost(&mut self) {
self.get_x_token();
}
fn begin_local_box(&mut self) {
let k = self.cur_chr;
self.push_saved(i64::from(k));
self.new_save_level(GroupCode::LocalBox);
self.scan_left_brace();
self.push_nest(Mode::RestrictedHorizontal);
self.cur_list_mut().space_factor = 1000;
}
fn finish_local_box(&mut self) {
self.unsave();
let k = self.pop_saved() as usize;
let head = self.cur_list().head;
let first = self.nodes.next(head);
self.nodes.set_next(head, NodeRef::NULL);
self.pop_nest();
let b = if first.is_null() {
NodeRef::NULL
} else {
let first = self.node_process_list(first, crate::nodeproc::Context::Hbox);
let b = self.hpack(first, PackSpec::NATURAL);
self.nodes.set_subtype(b, 2);
b
};
self.eq_define(Loc::LocalBox(k as u32), Val::Node(b));
if self.mode == Mode::Horizontal {
let lp = self.new_local_par();
self.tail_append(lp);
}
}
fn align_error(&mut self) {
if self.input.align_state.abs() > 2 {
self.print_err("Misplaced ");
self.print_cmd_chr(self.cur_cmd, self.cur_chr);
if self.cur_tok == Token::char(Cmd::TabMark, '&' as u32) {
self.help(&[
"I can't figure out why you would want to use a tab mark",
"here. If you just want an ampersand, the remedy is",
"simple: Just type `I\\&' now. But if some right brace",
"up above has ended a previous alignment prematurely,",
"you're probably due for more error messages, and you",
"might try typing `S' now just to see what is salvageable.",
]);
} else {
self.help(&[
"I can't figure out why you would want to use a tab mark",
"or \\cr or \\span just now. If something like a right brace",
"up above has ended a previous alignment prematurely,",
"you're probably due for more error messages, and you",
"might try typing `S' now just to see what is salvageable.",
]);
}
self.error();
} else {
self.back_input();
if self.input.align_state < 0 {
self.print_err("Missing { inserted");
self.input.align_state += 1;
self.cur_tok = Token::LEFT_BRACE;
} else {
self.print_err("Missing } inserted");
self.input.align_state -= 1;
self.cur_tok = Token::RIGHT_BRACE;
}
self.help(&[
"I've put in what seems to be necessary to fix",
"the current column of the current alignment.",
"Try to go on, since this might almost work.",
]);
self.ins_error();
}
}
fn no_align_error(&mut self) {
self.print_err("Misplaced ");
self.print_esc_str("noalign");
self.help(&[
"I expect to see \\noalign only after the \\cr of",
"an alignment. Proceed, and I'll ignore this case.",
]);
self.error();
}
fn omit_error(&mut self) {
self.print_err("Misplaced ");
self.print_esc_str("omit");
self.help(&[
"I expect to see \\omit only after tab marks or the \\cr of",
"an alignment. Proceed, and I'll ignore this case.",
]);
self.error();
}
fn cs_error(&mut self) {
self.print_err("Extra ");
self.print_esc_str("endcsname");
self.help(&["I'm ignoring this, since I wasn't doing a \\csname."]);
self.error();
}
pub fn append_to_vlist(&mut self, b: NodeRef) {
let pd = self.cur_list().prev_depth;
if pd > IGNORE_DEPTH {
let bs = self.eqtb.glue(gp::BASELINESKIP);
let h = self.node_height(b);
let d = bs.width - pd - h;
let p = if d < self.eqtb.dimen(dp::LINESKIPLIMIT) {
self.new_param_glue(glue_sub::LINE_SKIP)
} else {
let g = Glue { width: d, ..bs };
self.new_glue_node(g, glue_sub::BASELINE_SKIP)
};
self.tail_append(p);
}
self.tail_append(b);
let d = self.node_depth(b);
self.cur_list_mut().prev_depth = d;
}
pub fn handle_right_brace_typesetting(&mut self) {
match self.save.cur_group {
GroupCode::Hbox => self.package(false),
GroupCode::AdjustedHbox => {
self.start_adjust_lists();
self.package(false);
}
GroupCode::Vbox => {
if self.par_token_before_group_end(1) {
return;
}
self.end_graf();
self.package(false);
}
GroupCode::Vtop => {
if self.par_token_before_group_end(1) {
return;
}
self.end_graf();
self.package(true);
}
GroupCode::Insert => self.finish_insert_group(),
GroupCode::Output => self.resume_output(),
GroupCode::Disc => self.build_discretionary(),
GroupCode::Align => {
self.back_input();
self.cur_tok = Token::cs(self.cs.frozen_cr);
self.print_err("Missing ");
self.print_esc_str("cr");
self.printer.print_str(" inserted");
self.help(&["I'm guessing that you meant to end an alignment here."]);
self.ins_error();
}
GroupCode::NoAlign => {
if self.par_token_before_group_end(2) {
return;
}
self.end_graf();
self.unsave();
self.align_peek();
}
GroupCode::LocalBox => self.finish_local_box(),
GroupCode::Vcenter | GroupCode::Math | GroupCode::MathChoice => self.math_right_brace(),
_ => self.confusion("rightbrace"),
}
}
}
pub fn glue_param_of_subtype(sub: u16) -> u32 {
match sub {
glue_sub::LINE_SKIP => gp::LINESKIP,
glue_sub::BASELINE_SKIP => gp::BASELINESKIP,
glue_sub::PAR_SKIP => gp::PARSKIP,
glue_sub::ABOVE_DISPLAY => gp::ABOVEDISPLAYSKIP,
glue_sub::BELOW_DISPLAY => gp::BELOWDISPLAYSKIP,
glue_sub::ABOVE_DISPLAY_SHORT => gp::ABOVEDISPLAYSHORTSKIP,
glue_sub::BELOW_DISPLAY_SHORT => gp::BELOWDISPLAYSHORTSKIP,
glue_sub::LEFT_SKIP => gp::LEFTSKIP,
glue_sub::RIGHT_SKIP => gp::RIGHTSKIP,
glue_sub::TOP_SKIP => gp::TOPSKIP,
glue_sub::SPLIT_TOP_SKIP => gp::SPLITTOPSKIP,
glue_sub::TAB_SKIP => gp::TABSKIP,
glue_sub::SPACE_SKIP => gp::SPACESKIP,
glue_sub::XSPACE_SKIP => gp::XSPACESKIP,
glue_sub::PAR_FILL_SKIP => gp::PARFILLSKIP,
glue_sub::MATH_SKIP => gp::MATHSURROUNDSKIP,
_ => gp::LINESKIP,
}
}
pub fn box_with(width: i32, height: i32, depth: i32) -> BoxData {
BoxData {
width,
height,
depth,
..BoxData::default()
}
}