use oxipdf_ir::Dimension;
use oxipdf_ir::color::Color;
use oxipdf_ir::semantic::SemanticRole;
use oxipdf_ir::style::fragmentation::BreakValue;
use oxipdf_ir::style::list::{ListMarker, ListMarkerPosition, ListStyle};
use oxipdf_ir::style::typography::{FontStyle, LineHeight, TextAlign, WhiteSpace};
use oxipdf_ir::style::visual::BorderSide;
use oxipdf_ir::style::{Display, ResolvedStyle};
use oxipdf_ir::units::Pt;
use crate::Theme;
fn base_style(families: &[String], size: f64, weight: u16) -> ResolvedStyle {
let mut s = ResolvedStyle::default();
s.typography.font_families = families.to_vec();
s.typography.font_size = Pt::new(size);
s.typography.font_weight = weight;
s
}
fn with_margin_bottom(mut s: ResolvedStyle, pt: f64) -> ResolvedStyle {
s.layout.margin_bottom = Dimension::Length(Pt::new(pt));
s
}
pub(crate) fn default_theme() -> Theme {
let mut theme = Theme::new("Default");
let base = vec!["Noto Sans".to_string()];
let mono = vec!["Noto Sans Mono".to_string()];
theme.set_base_fonts(base.clone());
theme.set_mono_fonts(mono.clone());
{
let mut s = base_style(&base, 11.0, 400);
s.typography.line_height = LineHeight::Number(1.4);
s.typography.color = Color::rgb(0.13, 0.13, 0.13);
s.layout.display = Display::Block;
theme.set_style(SemanticRole::Document, s);
}
set_heading_styles(
&mut theme,
&base,
&[
(1, 28.0, 700, 32.0, 12.0),
(2, 22.0, 700, 24.0, 10.0),
(3, 17.0, 700, 20.0, 8.0),
(4, 14.0, 700, 16.0, 6.0),
(5, 12.0, 700, 14.0, 4.0),
(6, 11.0, 700, 12.0, 4.0),
],
);
{
let mut s = base_style(&base, 11.0, 400);
s.typography.line_height = LineHeight::Number(1.4);
s.layout.margin_bottom = Dimension::Length(Pt::new(6.0));
theme.set_style(SemanticRole::Paragraph, s);
}
{
let mut s = base_style(&mono, 10.0, 400);
s.typography.white_space = WhiteSpace::Pre;
s.typography.line_height = LineHeight::Number(1.3);
s.visual.background_color = Some(Color::rgb(0.96, 0.96, 0.96));
s.layout.padding_top = oxipdf_ir::LengthPercentage::Length(Pt::new(8.0));
s.layout.padding_right = oxipdf_ir::LengthPercentage::Length(Pt::new(8.0));
s.layout.padding_bottom = oxipdf_ir::LengthPercentage::Length(Pt::new(8.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(8.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(8.0));
s.visual.border_radius_top_left = Pt::new(3.0);
s.visual.border_radius_top_right = Pt::new(3.0);
s.visual.border_radius_bottom_right = Pt::new(3.0);
s.visual.border_radius_bottom_left = Pt::new(3.0);
theme.set_style(SemanticRole::CodeBlock, s);
}
{
let mut s = base_style(&base, 11.0, 400);
s.typography.font_style = FontStyle::Italic;
s.typography.color = Color::rgb(0.33, 0.33, 0.33);
s.layout.margin_top = Dimension::Length(Pt::new(8.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(8.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(12.0));
s.visual.border_left = BorderSide {
width: Pt::new(3.0),
style: oxipdf_ir::style::visual::BorderStyle::Solid,
color: Color::rgb(0.75, 0.75, 0.75),
};
theme.set_style(SemanticRole::BlockQuote, s);
}
{
let mut s = base_style(&base, 11.0, 400);
s.layout.margin_bottom = Dimension::Length(Pt::new(6.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(20.0));
theme.set_style(SemanticRole::List, s);
}
{
let mut s = base_style(&base, 11.0, 400);
s.typography.line_height = LineHeight::Number(1.4);
s.layout.margin_bottom = Dimension::Length(Pt::new(2.0));
s.list = ListStyle {
marker: Some(ListMarker::Disc),
position: ListMarkerPosition::Outside,
};
theme.set_style(SemanticRole::ListItem, s);
}
set_table_styles(&mut theme, &base);
{
let mut s = base_style(&base, 11.0, 400);
s.layout.margin_top = Dimension::Length(Pt::new(12.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(12.0));
s.layout.display = Display::Block;
theme.set_style(SemanticRole::Figure, s);
}
{
let mut s = base_style(&base, 10.0, 400);
s.typography.font_style = FontStyle::Italic;
s.typography.text_align = TextAlign::Center;
s.typography.color = Color::rgb(0.4, 0.4, 0.4);
s.layout.margin_top = Dimension::Length(Pt::new(4.0));
theme.set_style(SemanticRole::Caption, s);
}
{
let s = with_margin_bottom(base_style(&base, 11.0, 400), 12.0);
theme.set_style(SemanticRole::Section, s);
}
{
let mut s = base_style(&base, 9.0, 400);
s.typography.color = Color::rgb(0.4, 0.4, 0.4);
s.layout.margin_top = Dimension::Length(Pt::new(4.0));
theme.set_style(SemanticRole::Footnote, s);
}
{
let mut s = base_style(&base, 9.0, 400);
s.typography.color = Color::rgb(0.5, 0.5, 0.5);
theme.set_style(SemanticRole::PageDecoration, s);
}
{
let mut s = base_style(&base, 11.0, 400);
s.layout.margin_bottom = Dimension::Length(Pt::new(12.0));
theme.set_style(SemanticRole::Navigation, s);
}
theme
}
pub(crate) fn academic_theme() -> Theme {
let mut theme = Theme::new("Academic");
let base = vec!["Noto Serif".to_string()];
let mono = vec!["Noto Sans Mono".to_string()];
theme.set_base_fonts(base.clone());
theme.set_mono_fonts(mono.clone());
{
let mut s = base_style(&base, 11.0, 400);
s.typography.line_height = LineHeight::Number(1.5);
s.typography.color = Color::rgb(0.1, 0.1, 0.1);
s.layout.display = Display::Block;
theme.set_style(SemanticRole::Document, s);
}
set_heading_styles(
&mut theme,
&base,
&[
(1, 24.0, 700, 28.0, 10.0),
(2, 20.0, 700, 22.0, 8.0),
(3, 16.0, 700, 18.0, 6.0),
(4, 13.0, 700, 14.0, 6.0),
(5, 11.5, 700, 12.0, 4.0),
(6, 11.0, 700, 10.0, 4.0),
],
);
{
let mut s = base_style(&base, 11.0, 400);
s.typography.line_height = LineHeight::Number(1.5);
s.typography.text_align = TextAlign::Justify;
s.typography.text_indent = Pt::new(24.0);
s.layout.margin_bottom = Dimension::Length(Pt::new(4.0));
theme.set_style(SemanticRole::Paragraph, s);
}
{
let mut s = base_style(&mono, 9.5, 400);
s.typography.white_space = WhiteSpace::Pre;
s.typography.line_height = LineHeight::Number(1.3);
s.visual.background_color = Some(Color::rgb(0.97, 0.97, 0.97));
s.layout.padding_top = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
s.layout.padding_right = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
s.layout.padding_bottom = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(8.0));
set_all_borders(&mut s, 0.5, Color::rgb(0.8, 0.8, 0.8));
theme.set_style(SemanticRole::CodeBlock, s);
}
{
let mut s = base_style(&base, 10.5, 400);
s.typography.font_style = FontStyle::Italic;
s.layout.margin_top = Dimension::Length(Pt::new(8.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(8.0));
s.layout.margin_left = Dimension::Length(Pt::new(24.0));
s.layout.margin_right = Dimension::Length(Pt::new(24.0));
theme.set_style(SemanticRole::BlockQuote, s);
}
{
let mut s = base_style(&base, 11.0, 400);
s.layout.margin_bottom = Dimension::Length(Pt::new(6.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(24.0));
theme.set_style(SemanticRole::List, s);
}
{
let mut s = base_style(&base, 11.0, 400);
s.typography.line_height = LineHeight::Number(1.5);
s.layout.margin_bottom = Dimension::Length(Pt::new(2.0));
s.list = ListStyle {
marker: Some(ListMarker::Disc),
position: ListMarkerPosition::Outside,
};
theme.set_style(SemanticRole::ListItem, s);
}
set_table_styles(&mut theme, &base);
{
let mut s = base_style(&base, 11.0, 400);
s.layout.margin_top = Dimension::Length(Pt::new(16.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(16.0));
theme.set_style(SemanticRole::Figure, s);
}
{
let mut s = base_style(&base, 10.0, 400);
s.typography.text_align = TextAlign::Center;
s.layout.margin_top = Dimension::Length(Pt::new(6.0));
theme.set_style(SemanticRole::Caption, s);
}
{
let mut s = base_style(&base, 8.5, 400);
s.typography.color = Color::rgb(0.3, 0.3, 0.3);
s.layout.margin_top = Dimension::Length(Pt::new(2.0));
theme.set_style(SemanticRole::Footnote, s);
}
{
let mut s = base_style(&base, 9.0, 400);
s.typography.color = Color::rgb(0.45, 0.45, 0.45);
theme.set_style(SemanticRole::PageDecoration, s);
}
theme
}
pub(crate) fn technical_theme() -> Theme {
let mut theme = Theme::new("Technical");
let base = vec!["Noto Sans".to_string()];
let mono = vec!["Noto Sans Mono".to_string()];
theme.set_base_fonts(base.clone());
theme.set_mono_fonts(mono.clone());
{
let mut s = base_style(&base, 10.5, 400);
s.typography.line_height = LineHeight::Number(1.4);
s.typography.color = Color::rgb(0.15, 0.15, 0.15);
s.layout.display = Display::Block;
theme.set_style(SemanticRole::Document, s);
}
set_heading_styles(
&mut theme,
&base,
&[
(1, 24.0, 700, 28.0, 10.0),
(2, 18.0, 700, 20.0, 8.0),
(3, 14.0, 700, 16.0, 6.0),
(4, 12.0, 700, 12.0, 4.0),
(5, 11.0, 600, 10.0, 4.0),
(6, 10.5, 600, 8.0, 4.0),
],
);
{
let mut s = base_style(&base, 10.5, 400);
s.typography.line_height = LineHeight::Number(1.4);
s.layout.margin_bottom = Dimension::Length(Pt::new(6.0));
theme.set_style(SemanticRole::Paragraph, s);
}
{
let mut s = base_style(&mono, 9.5, 400);
s.typography.white_space = WhiteSpace::Pre;
s.typography.line_height = LineHeight::Number(1.35);
s.typography.color = Color::rgb(0.87, 0.87, 0.87);
s.visual.background_color = Some(Color::rgb(0.15, 0.16, 0.18));
s.layout.padding_top = oxipdf_ir::LengthPercentage::Length(Pt::new(10.0));
s.layout.padding_right = oxipdf_ir::LengthPercentage::Length(Pt::new(10.0));
s.layout.padding_bottom = oxipdf_ir::LengthPercentage::Length(Pt::new(10.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(10.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(10.0));
s.visual.border_radius_top_left = Pt::new(4.0);
s.visual.border_radius_top_right = Pt::new(4.0);
s.visual.border_radius_bottom_right = Pt::new(4.0);
s.visual.border_radius_bottom_left = Pt::new(4.0);
theme.set_style(SemanticRole::CodeBlock, s);
}
{
let mut s = base_style(&base, 10.5, 400);
s.typography.color = Color::rgb(0.35, 0.35, 0.35);
s.layout.margin_top = Dimension::Length(Pt::new(6.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(6.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(10.0));
s.visual.border_left = BorderSide {
width: Pt::new(3.0),
style: oxipdf_ir::style::visual::BorderStyle::Solid,
color: Color::rgb(0.3, 0.5, 0.9),
};
theme.set_style(SemanticRole::BlockQuote, s);
}
{
let mut s = base_style(&base, 10.5, 400);
s.layout.margin_bottom = Dimension::Length(Pt::new(6.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(20.0));
theme.set_style(SemanticRole::List, s);
}
{
let mut s = base_style(&base, 10.5, 400);
s.typography.line_height = LineHeight::Number(1.4);
s.layout.margin_bottom = Dimension::Length(Pt::new(2.0));
s.list = ListStyle {
marker: Some(ListMarker::Dash),
position: ListMarkerPosition::Outside,
};
theme.set_style(SemanticRole::ListItem, s);
}
set_table_styles(&mut theme, &base);
{
let mut s = base_style(&base, 10.5, 400);
s.layout.margin_top = Dimension::Length(Pt::new(10.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(10.0));
theme.set_style(SemanticRole::Figure, s);
}
{
let mut s = base_style(&mono, 9.0, 400);
s.typography.color = Color::rgb(0.4, 0.4, 0.4);
s.layout.margin_top = Dimension::Length(Pt::new(4.0));
theme.set_style(SemanticRole::Caption, s);
}
{
let mut s = base_style(&mono, 8.0, 400);
s.typography.color = Color::rgb(0.5, 0.5, 0.5);
theme.set_style(SemanticRole::PageDecoration, s);
}
theme
}
fn set_heading_styles(theme: &mut Theme, families: &[String], specs: &[(u8, f64, u16, f64, f64)]) {
for &(level, size, weight, mt, mb) in specs {
let mut s = base_style(families, size, weight);
s.typography.color = Color::rgb(0.1, 0.1, 0.1);
s.layout.margin_top = Dimension::Length(Pt::new(mt));
s.layout.margin_bottom = Dimension::Length(Pt::new(mb));
s.fragmentation.break_after = BreakValue::Avoid;
s.fragmentation.keep_with_next = true;
theme.set_style(SemanticRole::Heading { level }, s);
}
}
fn set_table_styles(theme: &mut Theme, families: &[String]) {
{
let mut s = base_style(families, 10.0, 400);
s.layout.margin_top = Dimension::Length(Pt::new(8.0));
s.layout.margin_bottom = Dimension::Length(Pt::new(8.0));
theme.set_style(SemanticRole::Table, s);
}
{
let mut s = base_style(families, 10.0, 700);
s.visual.background_color = Some(Color::rgb(0.93, 0.93, 0.93));
s.layout.padding_top = oxipdf_ir::LengthPercentage::Length(Pt::new(4.0));
s.layout.padding_right = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
s.layout.padding_bottom = oxipdf_ir::LengthPercentage::Length(Pt::new(4.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
theme.set_style(SemanticRole::TableHeader, s);
}
{
let s = base_style(families, 10.0, 400);
theme.set_style(SemanticRole::TableBody, s);
}
{
let mut s = base_style(families, 10.0, 400);
set_all_borders(&mut s, 0.5, Color::rgb(0.82, 0.82, 0.82));
theme.set_style(SemanticRole::TableRow, s);
}
{
let mut s = base_style(families, 10.0, 400);
s.layout.padding_top = oxipdf_ir::LengthPercentage::Length(Pt::new(4.0));
s.layout.padding_right = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
s.layout.padding_bottom = oxipdf_ir::LengthPercentage::Length(Pt::new(4.0));
s.layout.padding_left = oxipdf_ir::LengthPercentage::Length(Pt::new(6.0));
set_all_borders(&mut s, 0.5, Color::rgb(0.82, 0.82, 0.82));
theme.set_style(SemanticRole::TableCell, s);
}
}
fn set_all_borders(s: &mut ResolvedStyle, width: f64, color: Color) {
let side = BorderSide {
width: Pt::new(width),
style: oxipdf_ir::style::visual::BorderStyle::Solid,
color,
};
s.visual.border_top = side;
s.visual.border_right = side;
s.visual.border_bottom = side;
s.visual.border_left = side;
}