use crate::font::language::Language;
use crate::title::Title;
use crate::{font::tex_fonts::TexFonts, prelude::FontMetrics, tex};
pub use length::Length;
pub use margins::Margins;
pub use paper_size::PaperSize;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
pub use unit::Unit;
mod length;
mod margins;
mod paper_size;
mod unit;
#[derive(Clone, Deserialize, Serialize)]
pub struct Page {
pub paper_size: PaperSize,
pub margins: Margins,
pub column_separation: Length,
pub font_metrics: FontMetrics,
#[serde(skip, default = "get_default_table_width")]
pub(crate) table_width: f32,
#[serde(skip)]
pub(crate) preamble: Option<String>,
}
impl Page {
pub(crate) const END_DOCUMENT: &str = "\n\\end{sloppypar}\\end{document}";
pub(crate) fn set_table_width(&mut self) {
self.table_width =
self.paper_size.width() - (self.margins.left.get_pts() + self.margins.right.get_pts());
}
pub(crate) fn set_preamble(&mut self, title: &Option<Title>, fonts: &TexFonts) {
self.preamble = Some(Self::get_preamble(
title,
fonts,
&self.paper_size,
&self.margins,
&self.column_separation,
&self.font_metrics,
));
}
fn get_preamble(
title: &Option<Title>,
fonts: &TexFonts,
paper_size: &PaperSize,
margins: &Margins,
column_separation: &Length,
font_metrics: &FontMetrics,
) -> String {
let mut preamble = format!("\\documentclass[11pt, {}, openany]{{scrbook}}", paper_size);
preamble += &format!(
"\n\\usepackage[{}, {}]{{geometry}}\n\\pagenumbering{{gobble}}\n\n",
paper_size, margins
);
preamble += &["marginnote", "sectsty", "ragged2e", "paracol", "fontspec"]
.iter()
.map(|p| tex!("usepackage", p))
.collect::<Vec<String>>()
.join("\n");
let mut languages = HashSet::new();
languages.insert(fonts.left.language);
languages.insert(fonts.center.language);
languages.insert(fonts.right.language);
if let Some(title) = title.as_ref() {
languages.insert(title.language);
}
let polyglossia = languages
.iter()
.any(|language| *language != Language::English);
if polyglossia {
preamble.push('\n');
preamble += &tex!("usepackage", "polyglossia");
preamble.push('\n');
preamble += &tex!("setdefaultlanguage", "english");
preamble.push('\n');
let other_languages = languages
.into_iter()
.filter_map(|language| match language {
Language::English => None,
other => Some(other.to_string()),
})
.collect::<Vec<String>>();
if !other_languages.is_empty() {
preamble += &tex!("setotherlanguages", other_languages.join(","));
}
}
preamble += "\n\n\\allsectionsfont{\\centering}\n\\setlength\\parindent{";
preamble.push_str(&Length::pt(0.).to_string());
preamble.push('}');
for (keyword, length) in ["\\columnsep", "\\parfillskip"]
.iter()
.zip([column_separation, &Length::pt(0.)])
{
preamble += &Self::set_length(keyword, length)
}
preamble.push('\n');
for font in [&fonts.left, &fonts.center, &fonts.right].iter() {
preamble.push_str(&font.font_family);
preamble.push('\n');
}
preamble += "\n\n\\raggedbottom\n\n\\begin{document}\\begin{sloppypar}\n\n";
preamble + &tex!("fontsize", font_metrics.size, font_metrics.skip)
}
fn set_length(keyword: &str, length: &Length) -> String {
format!("\n{}", tex!("setlength", keyword, length))
}
#[cfg(feature = "default-fonts")]
fn default_column_separation() -> Length {
Length::inches(0.25)
}
}
#[cfg(feature = "default-fonts")]
impl Default for Page {
fn default() -> Self {
let margins = Margins::default();
let paper_size = PaperSize::default();
let font_metrics = FontMetrics::default();
let column_separation = Self::default_column_separation();
let table_width = get_default_table_width();
let preamble = Page::get_preamble(
&None,
&TexFonts::new().unwrap(),
&paper_size,
&margins,
&column_separation,
&font_metrics,
);
Self {
paper_size,
margins,
column_separation,
table_width,
preamble: Some(preamble),
font_metrics,
}
}
}
fn get_default_table_width() -> f32 {
let margins = Margins::default();
PaperSize::Letter.width() - (margins.left.get_pts() + margins.right.get_pts())
}