use crate::engine::nodes::RusTeXNode;
use crate::engine::stomach::RusTeXStomach;
use crate::engine::{register_command, Refs, Res, Types};
use crate::utils::VecMap;
use tex_engine::add_node;
use tex_engine::commands::{CommandScope, PrimitiveCommand, TeXCommand};
use tex_engine::engine::filesystem::SourceRef;
use tex_engine::engine::state::State;
use tex_engine::engine::stomach::Stomach;
use tex_engine::engine::DefaultEngine;
use tex_engine::prelude::*;
use tex_engine::tex::nodes::boxes::{HBoxInfo, TeXBox, ToOrSpread};
use tex_engine::tex::nodes::horizontal::HNode;
use tex_engine::tex::nodes::math::{MathAtom, MathKernel, MathNode, MathNucleus};
use tex_engine::tex::nodes::vertical::VNode;
use tex_engine::tex::tokens::CompactToken;
pub(crate) fn register_pgf(engine: &mut DefaultEngine<Types>) {
register_command(
engine,
true,
"pgfsysdriver",
"",
"pgfsys-rustex.def",
false,
false,
);
let all: &[(&'static str, fn(Refs, CompactToken) -> Res<()>)] = &[
("rustex!pgf!hbox", pgfhbox),
("rustex!pgf!literal", pgfliteral),
("rustex!pgf!gbegin", gbegin),
("rustex!pgf!gend", gend),
("rustex!pgf!begin", pgfbegin),
("rustex!pgf!end", pgfend),
];
for (s, c) in all {
engine.state.register_primitive(
&mut engine.aux,
s,
PrimitiveCommand::Unexpandable {
scope: CommandScope::Any, apply: *c,
},
);
}
engine.state.register_primitive(
&mut engine.aux,
"rustex!pgf!typesetpicturebox",
PrimitiveCommand::Unexpandable {
scope: CommandScope::SwitchesToHorizontalOrMath,
apply: pgftypesetpicturebox,
},
);
engine.state.register_primitive(
&mut engine.aux,
"rustex!pgf!flushpath",
PrimitiveCommand::Expandable(pgfflushpath),
);
register_command(
engine,
true,
"rustex!pgf!get!dimens",
"",
"=\\pgf@picminx =\\pgf@picminy =\\pgf@picmaxx =\\pgf@picmaxy",
true,
false,
);
}
fn pgfhbox(engine: Refs, tk: CompactToken) -> Res<()> {
let num = engine.read_register_index(false, &tk)?;
let bx = engine
.state
.take_box_register(num)
.unwrap_or_else(|| TeXBox::H {
info: HBoxInfo::new_box(ToOrSpread::None),
children: vec![].into(),
start: SourceRef::<Types>::default(),
end: SourceRef::<Types>::default(),
preskip: None,
});
let node = RusTeXNode::PGFEscape(bx);
add_node!(RusTeXStomach;engine, VNode::Custom(node),HNode::Custom(node),MathNode::Custom(node));
Ok(())
}
fn pgftypesetpicturebox(engine: Refs, tk: CompactToken) -> Res<()> {
let num = engine.read_register_index(false, &tk)?;
let bx = engine.state.take_box_register(num).unwrap();
let getdimens = engine
.aux
.memory
.cs_interner_mut()
.cs_from_str("rustex!pgf!get!dimens");
engine.requeue(CompactToken::from_cs(getdimens))?;
let minx = engine.read_dim(true, &tk)?;
let miny = engine.read_dim(true, &tk)?;
let maxx = engine.read_dim(true, &tk)?;
let maxy = engine.read_dim(true, &tk)?;
let (start, end) = if let TeXBox::H { start, end, .. } = &bx {
(start.clone(), end.clone())
} else {
unreachable!()
};
let node = RusTeXNode::PGFSvg {
bx,
minx,
miny,
maxx,
maxy,
};
match engine.stomach.data_mut().mode() {
TeXMode::RestrictedHorizontal | TeXMode::Horizontal => {
RusTeXStomach::add_node_h(engine, HNode::Custom(node))
}
_ => {
let info = HBoxInfo::new_box(ToOrSpread::None);
let bx = MathNode::Atom(MathAtom {
sub: None,
sup: None,
nucleus: MathNucleus::Inner(MathKernel::Box(TeXBox::H {
info,
children: vec![HNode::Custom(node)].into(),
start,
end,
preskip: None,
})),
});
RusTeXStomach::add_node_m(engine, bx)
}
}
Ok(())
}
fn pgfliteral(_engine: Refs, _token: CompactToken) -> Res<()> {
todo!("pgfliteral")
}
fn pgfflushpath(engine: Refs, ret: &mut Vec<CompactToken>, _token: CompactToken) -> Res<()> {
let path = engine
.aux
.memory
.cs_interner_mut()
.cs_from_str("pgf@sys@svgpath");
let empty = engine
.state
.get_command(
&engine
.aux
.memory
.cs_interner_mut()
.cs_from_str("pgfutil@empty"),
)
.cloned();
match engine.state.get_command(&path) {
Some(TeXCommand::Macro(m)) => ret.extend(m.expansion.0.iter().cloned()),
_ => todo!("error"),
};
engine.state.set_command(engine.aux, path, empty, true);
Ok(())
}
fn gbegin(engine: Refs, token: CompactToken) -> Res<()> {
let mut attrs: VecMap<&'static str, String> = VecMap::default();
let mut key = String::new();
engine.read_braced_string(true, true, &token, &mut key)?;
'attr: loop {
match engine.read_keywords(&[
b"about",
b"datatype",
b"href",
b"inlist",
b"prefix",
b"property",
b"rel",
b"resource",
b"rev",
b"src",
b"typeof",
b"content",
b"cx",
b"cy",
b"r",
b"rx",
b"ry",
b"clip-path",
b"fill-rule",
b"opacity",
b"stroke-opacity",
b"fill-opacity",
b"transform",
b"stroke-dasharray",
b"stroke-dashoffset",
b"stroke-width",
b"stroke",
b"fill",
b"id",
b"marker-start",
b"marker-end",
b"d",
b"fill",
b"visibility",
b"stroke-linecap",
b"stroke-linejoin",
b"href",
b"fx",
b"fy",
b"stroke-miterlimit",
b"patternUnits",
b"patternTransform",
b"markerUnits",
b"orient",
b"overflow",
b"attributeName",
b"from",
b"to",
b"stop-color",
b"style",
b"animateTransform",
b"animateMotion",
b"type",
b"gradientTransform",
b"offset",
b"width",
b"height",
b"dur",
b"restart",
b"repeatCount",
b"repeatDur",
b"begin",
b"end",
])? {
None => break 'attr,
Some(s) if s == b"animateTransform" => {
attrs.insert("animateTransform", "true".into());
}
Some(s) if s == b"animateMotion" => {
attrs.insert("animateMotion", "true".into());
}
Some(s) => {
let mut r = String::new();
engine.read_string(true, &mut r, &token)?;
attrs.insert(std::str::from_utf8(s).unwrap(), r.into());
}
}
}
let bg = RusTeXNode::PGFGBegin { attrs, tag: key };
add_node!(RusTeXStomach;engine, VNode::Custom(bg),HNode::Custom(bg),MathNode::Custom(bg));
Ok(())
}
fn gend(engine: Refs, token: CompactToken) -> Res<()> {
engine.skip_argument(&token)?;
add_node!(RusTeXStomach;engine, VNode::Custom(RusTeXNode::PGFGEnd),HNode::Custom(RusTeXNode::PGFGEnd),MathNode::Custom(RusTeXNode::PGFGEnd));
Ok(())
}
fn pgfbegin(_engine: Refs, _token: CompactToken) -> Res<()> {
todo!("pgfbegin")
}
fn pgfend(_engine: Refs, _token: CompactToken) -> Res<()> {
todo!("pgfend")
}