use std::rc::Rc;
use crate::Engine;
use crate::eqtb::{CodeKind, Loc, Meaning, Val};
use crate::format::{FormatError, Reader, Writer, cmd_of};
use crate::hash::{CsId, CsKind};
use crate::nodes::{
BoxData, FenceData, FractionData, GlyphData, LateData, NoadData, NodeData, NodeRef, Whatsit,
};
use crate::token::{Macro, Token, TokenList};
const MAGIC: &[u8] = b"retex figure 3\n";
pub struct Record {
pub line: i32,
pub names: Vec<(Vec<u8>, CsKind, Option<Meaning>)>,
pub fonts: Vec<(Vec<u8>, i32)>,
pub font_ids: Vec<(usize, CsId)>,
pub globals: Vec<(Loc, Val)>,
pub space_factor: i32,
pub us: u64,
pub nodes: NodeRef,
}
fn corrupt(what: &'static str) -> FormatError {
FormatError::Corrupt(what)
}
fn tokens(w: &mut Writer, t: &[Token]) {
w.u(t.len() as u64);
for tok in t {
w.u(tok.0.into());
}
}
fn read_tokens(r: &mut Reader) -> Result<TokenList, FormatError> {
let n = r.u()? as usize;
let mut v = Vec::with_capacity(n.min(1 << 20));
for _ in 0..n {
v.push(Token(r.u32()?));
}
Ok(Rc::from(v))
}
fn cs_kind(w: &mut Writer, k: &CsKind) -> Result<(), &'static str> {
match k {
CsKind::Active(c) => {
w.u(0);
w.u((*c).into());
}
CsKind::Null => w.u(1),
CsKind::Single(c) => {
w.u(2);
w.u((*c).into());
}
CsKind::Multi => w.u(3),
CsKind::Frozen => w.u(4),
_ => return Err("internal control sequences"),
}
Ok(())
}
fn read_cs_kind(r: &mut Reader) -> Result<CsKind, FormatError> {
Ok(match r.u()? {
0 => CsKind::Active(r.u32()?),
1 => CsKind::Null,
2 => CsKind::Single(r.u32()?),
3 => CsKind::Multi,
4 => CsKind::Frozen,
_ => return Err(corrupt("control sequence kind")),
})
}
fn meaning(w: &mut Writer, m: &Meaning) {
w.u(m.cmd as u64);
w.u(m.chr.into());
match &m.mac {
Some(mac) => {
w.u(1 + u64::from(mac.protected));
tokens(w, &mac.tokens);
}
None => w.u(0),
}
}
fn read_meaning(r: &mut Reader) -> Result<Meaning, FormatError> {
let cmd = cmd_of(r.u()? as u8)?;
let chr = r.u32()?;
let mac = match r.u()? {
0 => None,
k @ (1 | 2) => Some(Rc::new(Macro::new(k == 2, read_tokens(r)?))),
_ => return Err(corrupt("macro")),
};
Ok(Meaning { cmd, chr, mac })
}
fn code_kind(k: CodeKind) -> u64 {
match k {
CodeKind::Lc => 0,
CodeKind::Uc => 1,
CodeKind::Sf => 2,
CodeKind::Math => 3,
CodeKind::Del => 4,
}
}
fn loc(w: &mut Writer, l: Loc) {
let (tag, a, b) = match l {
Loc::Cs(cs) => (0, cs.0, 0),
Loc::IntPar(i) => (1, i, 0),
Loc::Count(i) => (2, i, 0),
Loc::Attr(i) => (3, i, 0),
Loc::DimenPar(i) => (4, i, 0),
Loc::Dimen(i) => (5, i, 0),
Loc::GluePar(i) => (6, i, 0),
Loc::Skip(i) => (7, i, 0),
Loc::MuSkip(i) => (8, i, 0),
Loc::ToksPar(i) => (9, i, 0),
Loc::Toks(i) => (10, i, 0),
Loc::Box(i) => (11, i, 0),
Loc::CurFont => (12, 0, 0),
Loc::MathFont(i) => (13, i, 0),
Loc::ParShape => (14, 0, 0),
Loc::EtexShape(i) => (15, i, 0),
Loc::Cat { table, ch } => (16, table, ch),
Loc::Code(k, ch) => (17, code_kind(k) as u32, ch),
Loc::MathParam(i) => (18, i, 0),
#[allow(unreachable_patterns)]
_ => (u64::MAX, 0, 0),
};
w.u(tag);
w.u(a.into());
w.u(b.into());
}
fn read_loc(r: &mut Reader) -> Result<Loc, FormatError> {
let tag = r.u()?;
let a = r.u32()?;
let b = r.u32()?;
Ok(match tag {
0 => Loc::Cs(CsId(a)),
1 => Loc::IntPar(a),
2 => Loc::Count(a),
3 => Loc::Attr(a),
4 => Loc::DimenPar(a),
5 => Loc::Dimen(a),
6 => Loc::GluePar(a),
7 => Loc::Skip(a),
8 => Loc::MuSkip(a),
9 => Loc::ToksPar(a),
10 => Loc::Toks(a),
11 => Loc::Box(a),
12 => Loc::CurFont,
13 => Loc::MathFont(a),
14 => Loc::ParShape,
15 => Loc::EtexShape(a),
16 => Loc::Cat { table: a, ch: b },
17 => Loc::Code(
match a {
0 => CodeKind::Lc,
1 => CodeKind::Uc,
2 => CodeKind::Sf,
3 => CodeKind::Math,
4 => CodeKind::Del,
_ => return Err(corrupt("code table")),
},
b,
),
18 => Loc::MathParam(a),
_ => return Err(corrupt("location")),
})
}
impl Engine {
pub(crate) fn figure_bytes(&self, key: u64, rec: &Record) -> Result<Vec<u8>, &'static str> {
let mut w = Writer { buf: Vec::new() };
w.buf.extend_from_slice(MAGIC);
w.bytes(env!("CARGO_PKG_VERSION").as_bytes());
w.u(key);
w.i(rec.line.into());
w.u(rec.names.len() as u64);
for (name, kind, m) in &rec.names {
w.bytes(name);
cs_kind(&mut w, kind)?;
match m {
Some(m) => {
w.u(1);
meaning(&mut w, m);
}
None => w.u(0),
}
}
w.u(rec.fonts.len() as u64);
for (name, size) in &rec.fonts {
w.bytes(name);
w.i((*size).into());
}
w.u(rec.font_ids.len() as u64);
for (f, cs) in &rec.font_ids {
w.u(*f as u64);
w.u(cs.0.into());
}
w.u(rec.globals.len() as u64);
for (l, v) in &rec.globals {
loc(&mut w, *l);
match v {
Val::Meaning(m) => {
w.u(0);
meaning(&mut w, m);
}
Val::Int(i) => {
w.u(1);
w.i(*i);
}
Val::Glue(g) => {
w.u(2);
w.glue(g);
}
Val::Toks(t) => {
w.u(3);
tokens(&mut w, t);
}
Val::Node(p) => {
w.u(4);
self.write_list(&mut w, *p)?;
}
Val::Shape(s) => {
w.u(5);
match s {
Some(s) => {
w.u(s.len() as u64 + 1);
for v in s.iter() {
w.i((*v).into());
}
}
None => w.u(0),
}
}
}
}
w.i(rec.space_factor.into());
w.u(rec.us);
self.write_list(&mut w, rec.nodes)?;
Ok(w.buf)
}
fn write_list(&self, w: &mut Writer, mut p: NodeRef) -> Result<(), &'static str> {
let mut items = Vec::new();
while !p.is_null() {
items.push(p);
p = self.nodes.next(p);
}
w.u(items.len() as u64);
for p in items {
self.write_node(w, p)?;
}
Ok(())
}
fn write_node(&self, w: &mut Writer, p: NodeRef) -> Result<(), &'static str> {
let n = self.nodes.get(p);
w.u(n.id.into());
w.u(n.subtype.into());
match &n.attr {
Some(a) => {
w.u(a.len() as u64 + 1);
for (k, v) in a.iter() {
w.u((*k).into());
w.i((*v).into());
}
}
None => w.u(0),
}
if self.nodes.sync.is_some() {
let (tag, line) = self.nodes.sync_fields(p);
w.u(tag.into());
w.i(line.into());
}
match &n.data {
NodeData::Box(b) => {
w.u(0);
for v in [b.width, b.depth, b.height, b.shift] {
w.i(v.into());
}
w.i(b.dir.into());
w.u(b.glue_order.into());
w.u(b.glue_sign.into());
w.u(b.glue_set.to_bits());
self.write_list(w, b.head)?;
}
NodeData::Rule {
width,
depth,
height,
dir,
index,
left,
right,
} => {
w.u(1);
for v in [*width, *depth, *height] {
w.i(v.into());
}
w.i((*dir).into());
for v in [*index, *left, *right] {
w.i(v.into());
}
}
NodeData::Mark { class, mark } => {
w.u(2);
w.u((*class).into());
tokens(w, mark);
}
NodeData::Boundary { value } => {
w.u(3);
w.i((*value).into());
}
NodeData::Disc {
pre,
post,
replace,
penalty,
} => {
w.u(4);
self.write_list(w, *pre)?;
self.write_list(w, *post)?;
self.write_list(w, *replace)?;
w.i((*penalty).into());
}
NodeData::Whatsit(wh) => {
w.u(5);
self.write_whatsit(w, wh)?;
}
NodeData::Dir { dir, level } => {
w.u(6);
w.i((*dir).into());
w.i((*level).into());
}
NodeData::Math { surround, glue } => {
w.u(7);
w.i((*surround).into());
w.glue(glue);
}
NodeData::Glue { spec, leader } => {
w.u(8);
w.glue(spec);
self.write_list(w, *leader)?;
}
NodeData::Kern {
kern,
expansion_factor,
} => {
w.u(9);
w.i((*kern).into());
w.i((*expansion_factor).into());
}
NodeData::Penalty { penalty } => {
w.u(10);
w.i((*penalty).into());
}
NodeData::Glyph(g) => {
w.u(11);
w.u(g.chr.into());
w.u(g.font.into());
w.u(g.lang.into());
w.u(g.left.into());
w.u(g.right.into());
w.u(g.uchyph.into());
self.write_list(w, g.components)?;
for v in [g.xoffset, g.yoffset, g.expansion_factor, g.data] {
w.i(v.into());
}
}
NodeData::MarginKern { width, glyph } => {
w.u(12);
w.i((*width).into());
self.write_list(w, *glyph)?;
}
NodeData::LocalPar {
pen_inter,
pen_broken,
dir,
box_left,
box_left_width,
box_right,
box_right_width,
} => {
w.u(21);
w.i((*pen_inter).into());
w.i((*pen_broken).into());
w.i((*dir).into());
self.write_list(w, *box_left)?;
w.i((*box_left_width).into());
self.write_list(w, *box_right)?;
w.i((*box_right_width).into());
}
NodeData::Noad(d) => {
w.u(13);
for l in [
d.nucleus,
d.sub,
d.sup,
d.left,
d.degree,
d.accent,
d.bot_accent,
d.new_hlist,
] {
self.write_list(w, l)?;
}
w.u(d.options.into());
w.i(d.width.into());
w.i(d.fraction.into());
}
NodeData::Fraction(d) => {
w.u(14);
w.i(d.thickness.into());
for l in [d.num, d.denom, d.left, d.right, d.middle, d.new_hlist] {
self.write_list(w, l)?;
}
w.u(d.options.into());
}
NodeData::Fence(d) => {
w.u(15);
self.write_list(w, d.delim)?;
for v in [d.italic, d.height, d.depth] {
w.i(v.into());
}
w.u(d.options.into());
w.i(d.class.into());
self.write_list(w, d.new_hlist)?;
}
NodeData::Style { style } => {
w.u(16);
w.u((*style).into());
}
NodeData::Choice(c) => {
w.u(17);
for l in c.iter() {
self.write_list(w, *l)?;
}
}
NodeData::MathChar { fam, chr } => {
w.u(18);
w.u((*fam).into());
w.u((*chr).into());
}
NodeData::SubList { head } => {
w.u(19);
self.write_list(w, *head)?;
}
NodeData::Delim {
small_fam,
small_char,
large_fam,
large_char,
} => {
w.u(20);
for v in [*small_fam, *small_char, *large_fam, *large_char] {
w.u(v.into());
}
}
other => {
if std::env::var_os("RETEX_FIGURES_DEBUG").is_some() {
eprintln!("[fig] node {} {:?}", n.id, std::mem::discriminant(other));
}
return Err("nodes the cache does not hold");
}
}
Ok(())
}
fn write_whatsit(&self, w: &mut Writer, wh: &Whatsit) -> Result<(), &'static str> {
let late = |w: &mut Writer, d: &LateData| -> Result<(), &'static str> {
match d {
LateData::Tokens(t) => {
w.u(0);
tokens(w, t);
}
LateData::Bytes(b) => {
w.u(1);
w.bytes(b);
}
_ => return Err("Lua data in its nodes"),
}
Ok(())
};
match wh {
Whatsit::Open { stream, name } => {
w.u(0);
w.u((*stream).into());
w.bytes(name);
}
Whatsit::Write { stream, data } => {
w.u(1);
w.i((*stream).into());
tokens(w, data);
}
Whatsit::Close { stream } => {
w.u(2);
w.u((*stream).into());
}
Whatsit::Special { data } => {
w.u(3);
w.bytes(data);
}
Whatsit::LateLua { data, name } => {
w.u(4);
late(w, data)?;
w.bytes(name);
}
Whatsit::PdfLiteral {
mode,
data,
late: l,
} => {
w.u(5);
w.u((*mode).into());
late(w, data)?;
w.u((*l).into());
}
Whatsit::SavePos => w.u(6),
Whatsit::PdfColorstack { stack, cmd, data } => {
w.u(7);
w.i((*stack).into());
w.u((*cmd).into());
w.bytes(data);
}
Whatsit::PdfSetmatrix { data } => {
w.u(8);
w.bytes(data);
}
Whatsit::PdfSave => w.u(9),
Whatsit::PdfRestore => w.u(10),
Whatsit::PdfLinkState { state } => {
w.u(11);
w.i((*state).into());
}
other => {
if std::env::var_os("RETEX_FIGURES_DEBUG").is_some() {
eprintln!("[fig] whatsit {other:?}");
}
return Err("nodes the cache does not hold");
}
}
Ok(())
}
pub(crate) fn figure_from_bytes(
&mut self,
data: &[u8],
key: u64,
tag: u32,
line: i32,
) -> Result<Record, FormatError> {
let body = data.strip_prefix(MAGIC).ok_or(FormatError::BadHeader)?;
let mut r = Reader::new(body);
if r.bytes()? != env!("CARGO_PKG_VERSION").as_bytes() || r.u()? != key {
return Err(FormatError::BadHeader);
}
let recorded_line = r.i32()?;
let n = r.u()? as usize;
let mut names = Vec::with_capacity(n.min(1 << 16));
for _ in 0..n {
let name = r.bytes()?.to_vec();
let kind = read_cs_kind(&mut r)?;
let m = match r.u()? {
0 => None,
_ => Some(read_meaning(&mut r)?),
};
names.push((name, kind, m));
}
let n = r.u()? as usize;
let mut fonts = Vec::with_capacity(n.min(1 << 10));
for _ in 0..n {
fonts.push((r.bytes()?.to_vec(), r.i32()?));
}
let n = r.u()? as usize;
let mut font_ids = Vec::with_capacity(n.min(1 << 10));
for _ in 0..n {
font_ids.push((r.u()? as usize, CsId(r.u32()?)));
}
let delta = line - recorded_line;
let n = r.u()? as usize;
let mut globals = Vec::with_capacity(n.min(1 << 16));
for _ in 0..n {
let l = read_loc(&mut r)?;
let v = match r.u()? {
0 => Val::Meaning(read_meaning(&mut r)?),
1 => Val::Int(r.i()?),
2 => Val::Glue(r.glue()?),
3 => Val::Toks(read_tokens(&mut r)?),
4 => Val::Node(self.read_list(&mut r, tag, delta)?),
5 => match r.u()? as usize {
0 => Val::Shape(None),
k => {
let mut s = Vec::with_capacity((k - 1).min(1 << 16));
for _ in 1..k {
s.push(r.i32()?);
}
Val::Shape(Some(Rc::from(s)))
}
},
_ => return Err(corrupt("value")),
};
globals.push((l, v));
}
let space_factor = r.i32()?;
let us = r.u()?;
let nodes = self.read_list(&mut r, tag, delta)?;
Ok(Record {
line,
names,
fonts,
font_ids,
globals,
space_factor,
us,
nodes,
})
}
fn read_list(&mut self, r: &mut Reader, tag: u32, delta: i32) -> Result<NodeRef, FormatError> {
let n = r.u()? as usize;
let mut head = NodeRef::NULL;
let mut tail = NodeRef::NULL;
for _ in 0..n {
let p = self.read_node(r, tag, delta)?;
if tail.is_null() {
head = p;
} else {
self.nodes.link(tail, p);
}
tail = p;
}
Ok(head)
}
fn read_node(&mut self, r: &mut Reader, tag: u32, delta: i32) -> Result<NodeRef, FormatError> {
let id = r.u()? as u8;
let subtype = r.u()? as u16;
let attr = match r.u()? as usize {
0 => None,
k => {
let mut a = Vec::with_capacity((k - 1).min(1 << 10));
for _ in 1..k {
a.push((r.u32()?, r.i32()?));
}
Some(Rc::from(a))
}
};
let sync = if self.nodes.sync.is_some() {
let t = r.u32()?;
let l = r.i32()?;
Some((t, if t == tag && l > 0 { l + delta } else { l }))
} else {
None
};
let data = match r.u()? {
0 => {
let (width, depth, height, shift) = (r.i32()?, r.i32()?, r.i32()?, r.i32()?);
let dir = r.i32()? as i8;
let glue_order = r.u()? as u8;
let glue_sign = r.u()? as u8;
let glue_set = f64::from_bits(r.u()?);
let head = self.read_list(r, tag, delta)?;
NodeData::Box(Box::new(BoxData {
width,
depth,
height,
dir,
shift,
glue_order,
glue_sign,
glue_set,
head,
}))
}
1 => {
let (width, depth, height) = (r.i32()?, r.i32()?, r.i32()?);
let dir = r.i32()? as i8;
let (index, left, right) = (r.i32()?, r.i32()?, r.i32()?);
NodeData::Rule {
width,
depth,
height,
dir,
index,
left,
right,
}
}
2 => NodeData::Mark {
class: r.u32()?,
mark: read_tokens(r)?,
},
3 => NodeData::Boundary { value: r.i32()? },
4 => {
let pre = self.read_list(r, tag, delta)?;
let post = self.read_list(r, tag, delta)?;
let replace = self.read_list(r, tag, delta)?;
NodeData::Disc {
pre,
post,
replace,
penalty: r.i32()?,
}
}
5 => NodeData::Whatsit(Box::new(read_whatsit(r)?)),
6 => NodeData::Dir {
dir: r.i32()? as i8,
level: r.i32()?,
},
7 => NodeData::Math {
surround: r.i32()?,
glue: r.glue()?,
},
8 => {
let spec = r.glue()?;
let leader = self.read_list(r, tag, delta)?;
NodeData::Glue { spec, leader }
}
9 => NodeData::Kern {
kern: r.i32()?,
expansion_factor: r.i32()?,
},
10 => NodeData::Penalty { penalty: r.i32()? },
11 => {
let chr = r.u32()?;
let font = r.u32()?;
let lang = r.u()? as u16;
let left = r.u()? as u8;
let right = r.u()? as u8;
let uchyph = r.u()? != 0;
let components = self.read_list(r, tag, delta)?;
let (xoffset, yoffset, expansion_factor, data) =
(r.i32()?, r.i32()?, r.i32()?, r.i32()?);
NodeData::Glyph(GlyphData {
chr,
font,
lang,
left,
right,
uchyph,
components,
xoffset,
yoffset,
expansion_factor,
data,
})
}
12 => {
let width = r.i32()?;
let glyph = self.read_list(r, tag, delta)?;
NodeData::MarginKern { width, glyph }
}
13 => {
let mut l = [NodeRef::NULL; 8];
for x in &mut l {
*x = self.read_list(r, tag, delta)?;
}
NodeData::Noad(Box::new(NoadData {
nucleus: l[0],
sub: l[1],
sup: l[2],
left: l[3],
degree: l[4],
accent: l[5],
bot_accent: l[6],
new_hlist: l[7],
options: r.u32()?,
width: r.i32()?,
fraction: r.i32()?,
}))
}
14 => {
let thickness = r.i32()?;
let mut l = [NodeRef::NULL; 6];
for x in &mut l {
*x = self.read_list(r, tag, delta)?;
}
NodeData::Fraction(Box::new(FractionData {
thickness,
num: l[0],
denom: l[1],
left: l[2],
right: l[3],
middle: l[4],
new_hlist: l[5],
options: r.u32()?,
}))
}
15 => {
let delim = self.read_list(r, tag, delta)?;
let (italic, height, depth) = (r.i32()?, r.i32()?, r.i32()?);
let options = r.u32()?;
let class = r.i32()?;
let new_hlist = self.read_list(r, tag, delta)?;
NodeData::Fence(Box::new(FenceData {
delim,
italic,
height,
depth,
options,
class,
new_hlist,
}))
}
16 => NodeData::Style {
style: r.u()? as u8,
},
17 => {
let mut l = [NodeRef::NULL; 4];
for x in &mut l {
*x = self.read_list(r, tag, delta)?;
}
NodeData::Choice(Box::new(l))
}
18 => NodeData::MathChar {
fam: r.u32()?,
chr: r.u32()?,
},
19 => NodeData::SubList {
head: self.read_list(r, tag, delta)?,
},
20 => NodeData::Delim {
small_fam: r.u32()?,
small_char: r.u32()?,
large_fam: r.u32()?,
large_char: r.u32()?,
},
21 => {
let pen_inter = r.i32()?;
let pen_broken = r.i32()?;
let dir = r.i32()? as i8;
let box_left = self.read_list(r, tag, delta)?;
let box_left_width = r.i32()?;
let box_right = self.read_list(r, tag, delta)?;
NodeData::LocalPar {
pen_inter,
pen_broken,
dir,
box_left,
box_left_width,
box_right,
box_right_width: r.i32()?,
}
}
_ => return Err(corrupt("node")),
};
let p = self.nodes.alloc(id, subtype, data);
self.nodes.get_mut(p).attr = attr;
if let Some(f) = sync {
self.nodes.set_sync_fields(p, f);
}
Ok(p)
}
}
fn read_whatsit(r: &mut Reader) -> Result<Whatsit, FormatError> {
let late = |r: &mut Reader| -> Result<LateData, FormatError> {
Ok(match r.u()? {
0 => LateData::Tokens(read_tokens(r)?),
1 => LateData::Bytes(r.bytes()?.to_vec()),
_ => return Err(corrupt("late data")),
})
};
Ok(match r.u()? {
0 => Whatsit::Open {
stream: r.u32()?,
name: r.bytes()?.to_vec(),
},
1 => Whatsit::Write {
stream: r.i32()?,
data: read_tokens(r)?,
},
2 => Whatsit::Close { stream: r.u32()? },
3 => Whatsit::Special {
data: r.bytes()?.to_vec(),
},
4 => {
let data = late(r)?;
Whatsit::LateLua {
data,
name: r.bytes()?.to_vec(),
}
}
5 => {
let mode = r.u()? as u8;
let data = late(r)?;
Whatsit::PdfLiteral {
mode,
data,
late: r.u()? != 0,
}
}
6 => Whatsit::SavePos,
7 => Whatsit::PdfColorstack {
stack: r.i32()?,
cmd: (r.u()? as u8),
data: r.bytes()?.to_vec(),
},
8 => Whatsit::PdfSetmatrix {
data: r.bytes()?.to_vec(),
},
9 => Whatsit::PdfSave,
10 => Whatsit::PdfRestore,
11 => Whatsit::PdfLinkState { state: r.i32()? },
_ => return Err(corrupt("whatsit")),
})
}