#![doc = include_str!("../doc/README_rs.md")]
#![cfg_attr(all(),
doc = embed_doc_image::embed_image!("daf", "images/daf.jpg"),
doc = embed_doc_image::embed_image!("four_rows", "images/four_rows.jpg"),
doc = embed_doc_image::embed_image!("center", "images/center.jpg"))]
use std::{fs::read, path::Path};
use error::Error;
use serde::{Deserialize, Serialize};
use serde_json::from_slice;
use text::{Daf, SourceText};
use xetex::get_pdf;
use crate::{
font::fonts::Fonts,
page::Page,
span::Span,
table::{maybe_span_column::MaybeSpanColumn, span_column::SpanColumn, OptionalColumn, Table},
};
mod error;
mod font;
mod page;
pub mod prelude;
mod span;
mod table;
mod text;
mod title;
#[cfg(not(feature = "textest"))]
pub(crate) mod xetex;
#[cfg(feature = "textest")]
pub use crate::font::default_tex_fonts::DefaultTexFonts;
use crate::title::Title;
#[cfg(feature = "textest")]
pub mod xetex;
#[macro_export]
macro_rules! tex {
($command:expr, $($value:expr),+) => {
{
let mut t = format!("\\{}", &$command);
$(
t.push_str(&format!("{{{}}}", &$value));
)+
t
}
};
}
#[cfg_attr(feature = "default-fonts", derive(Default))]
#[derive(Deserialize, Serialize)]
pub struct Talmudifier {
page: Page,
#[cfg_attr(feature = "default-fonts", serde(default = "Fonts::default"))]
fonts: Fonts,
source_text: SourceText,
title: Option<Title>,
log: bool,
}
impl Talmudifier {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
match read(path) {
Ok(text) => match from_slice(&text) {
Ok(config) => Ok(config),
Err(error) => Err(Error::ConfigSerde(error)),
},
Err(error) => Err(Error::ConfigRead(error)),
}
}
pub fn page(mut self, page: Page) -> Self {
self.page = page;
self
}
pub fn fonts(mut self, fonts: Fonts) -> Self {
self.fonts = fonts;
self
}
pub fn source_text(mut self, source_text: SourceText) -> Self {
self.source_text = source_text;
self
}
pub fn title(mut self, title: Title) -> Self {
self.title = Some(title);
self
}
pub fn log(mut self) -> Self {
self.log = true;
self
}
pub fn talmudify(&self) -> Result<Daf, Error> {
let tex_fonts = self.fonts.tex_fonts()?;
let mut page = self.page.clone();
page.set_preamble(&self.title, &tex_fonts);
page.set_table_width();
page.set_preamble(&self.title, &tex_fonts);
let raw_text = self.source_text.get_text()?;
let left_span = Span::from_md(&raw_text.left)?;
let center_span = Span::from_md(&raw_text.center)?;
let right_span = Span::from_md(&raw_text.right)?;
let cosmic_fonts = self.fonts.cosmic_fonts(&page.font_metrics)?;
let mut left = SpanColumn::new(
left_span,
cosmic_fonts.left,
&tex_fonts.left.command,
tex_fonts.left.language,
);
let mut center = SpanColumn::new(
center_span,
cosmic_fonts.center,
&tex_fonts.center.command,
tex_fonts.center.language,
);
let mut right = SpanColumn::new(
right_span,
cosmic_fonts.right,
&tex_fonts.right.command,
tex_fonts.right.language,
);
let mut tables = vec![];
let mut table = Table::new(
Some(MaybeSpanColumn::Span(&mut left)),
None,
Some(MaybeSpanColumn::Span(&mut right)),
&page,
self.log,
);
let mut done = false;
match table.get_tex_table(None, 4)? {
Some(table) => {
tables.push(table);
}
None => done = true,
}
if !done {
done = table.done();
}
if !done {
table = Table::new(
Some(MaybeSpanColumn::Span(&mut left)),
Some(MaybeSpanColumn::Empty),
Some(MaybeSpanColumn::Span(&mut right)),
&page,
self.log,
);
match table.get_tex_table(None, 1)? {
Some(table) => tables.push(table),
None => done = true,
}
}
if !done {
done = table.done();
}
if !done {
if let Some(title) = &self.title {
table = Table::new(
Some(MaybeSpanColumn::Span(&mut left)),
Some(MaybeSpanColumn::Empty),
Some(MaybeSpanColumn::Span(&mut right)),
&page,
self.log,
);
match table.get_title_table(title)? {
Some(table) => tables.push(table),
None => done = true,
}
}
}
if !done {
done = table.done();
}
while !done {
let left_column = Self::get_column(&mut left);
let center_column = Self::get_column(&mut center);
let right_column = Self::get_column(&mut right);
let was_done = [&left_column, ¢er_column, &right_column]
.iter()
.map(|c| c.is_none())
.collect::<Vec<bool>>();
table = Table::new(left_column, center_column, right_column, &page, self.log);
let (num_lines, position) = table.get_min_num_lines()?;
match num_lines {
Some(num_lines) =>
{
match table.get_tex_table(Some(position), num_lines)? {
Some(table) => tables.push(table),
None => done = true,
}
}
None => {
tables.push(table.get_tex_table_one_column());
done = true;
}
}
if !done {
done = table.done();
}
if !done {
table = Table::new(
Self::get_skip_column(&mut left, was_done[0]),
Self::get_skip_column(&mut center, was_done[1]),
Self::get_skip_column(&mut right, was_done[2]),
&page,
self.log,
);
match table.get_tex_table(None, 1)? {
Some(table) => tables.push(table),
None => done = true,
}
}
if !done {
done = table.done();
}
}
let mut tex = page.preamble.clone().unwrap();
tex.push_str(&tables.join("\n"));
tex.push_str(Page::END_DOCUMENT);
let pdf = get_pdf(&tex)?;
Ok(Daf { tex, pdf })
}
fn get_column(span_column: &mut SpanColumn) -> OptionalColumn<'_> {
if span_column.done() {
None
} else {
Some(MaybeSpanColumn::Span(span_column))
}
}
fn get_skip_column(span_column: &mut SpanColumn, was_done: bool) -> OptionalColumn<'_> {
if span_column.done() {
if was_done {
None
} else {
Some(MaybeSpanColumn::Empty)
}
} else {
Some(MaybeSpanColumn::Span(span_column))
}
}
}
#[cfg(test)]
mod tests {
use serde_json::from_slice;
use std::fs::write;
use crate::{get_pdf, Talmudifier};
pub(crate) fn get_test_md() -> (String, String, String) {
let raw = include_str!("../test_text/test.md")
.split("\n\n")
.collect::<Vec<&str>>();
assert_eq!(raw.len(), 3);
(raw[0].to_string(), raw[1].to_string(), raw[2].to_string())
}
#[test]
fn test_tex() {
for (tex, path) in [
include_str!("../test_text/hello_world.tex"),
include_str!("../test_text/minimal_daf.tex"),
include_str!("../test_text/paracol.tex"),
include_str!("../test_text/daf.tex"),
]
.iter()
.zip(["hello_world", "minimal_daf", "paracol", "daf"])
{
if let Err(error) = get_pdf(&tex.replace("\r", "")) {
panic!("Tex error: {} {}", error, path)
}
}
}
#[test]
fn from_example_json() {
from_slice::<Talmudifier>(include_bytes!("../example_talmudifier.json")).unwrap();
}
#[test]
fn test_hebrew() {
let daf = Talmudifier::new("test_hebrew/config.json")
.unwrap()
.talmudify()
.unwrap();
write("hebrew.pdf", daf.pdf).unwrap();
write("hebrew.tex", daf.tex).unwrap();
}
}