//! A module for writing intros and outros use crate::{time::Date, ProjectLecture, Resolution}; use anyhow::anyhow; use std::{ fmt::{self, Debug, Display, Formatter}, str::FromStr }; use svgwriter::{ tags::{Group, Rect, TagWithPresentationAttributes, Text}, Graphic }; #[derive(Clone)] pub struct Language<'a> { lang: &'a str, format_date_long: fn(Date) -> String, // intro lecture_from: &'a str, video_created_by_us: &'a str, // outro video_created_by: &'a str, our_website: &'a str, download_videos: &'a str, questions_feedback: &'a str } pub const GERMAN: Language<'static> = Language { lang: "de", // Format a date in DD. MMMM YYYY format. format_date_long: |d: Date| { let month = match d.month { 1 => "Januar", 2 => "Februar", 3 => "März", 4 => "April", 5 => "Mai", 6 => "Juni", 7 => "Juli", 8 => "August", 9 => "September", 10 => "Oktober", 11 => "November", 12 => "Dezember", _ => unreachable!() }; format!("{:02}. {month} {:04}", d.day, d.year) }, lecture_from: "Vorlesung vom", video_created_by_us: "Video erstellt von der Video AG, Fachschaft I/1", video_created_by: "Video erstellt von der", our_website: "Website der Fachschaft", download_videos: "Videos herunterladen", questions_feedback: "Fragen, Vorschläge und Feedback" }; pub const BRITISH: Language<'static> = Language { lang: "uk", // Format a date in DDth MMMM YYYY format. format_date_long: |d: Date| { let month = match d.month { 1 => "January", 2 => "February", 3 => "March", 4 => "April", 5 => "May", 6 => "June", 7 => "July", 8 => "August", 9 => "September", 10 => "October", 11 => "November", 12 => "December", _ => unreachable!() }; let th = match d.day { 1 | 21 | 31 => "st", 2 | 22 => "nd", 3 | 23 => "rd", _ => "th" }; format!("{:02}{th} {month} {:04}", d.day, d.year) }, lecture_from: "Lecture from", video_created_by_us: "Video created by the Video AG, Fachschaft I/1", video_created_by: "Video created by the", our_website: "The Fachschaft's website", download_videos: "Download videos", questions_feedback: "Questions, Suggestions and Feedback" }; impl FromStr for Language<'static> { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "de" => Ok(GERMAN), "en" | "uk" => Ok(BRITISH), lang => Err(anyhow!("Unknown language {lang:?}")) } } } impl Display for Language<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str(self.lang) } } impl Debug for Language<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Language") .field("lang", &self.lang) .finish_non_exhaustive() } } #[repr(u16)] enum FontSize { Huge = 72, Large = 56, Big = 44 } #[repr(u16)] enum FontWeight { Normal = 400, SemiBold = 500, Bold = 700 } struct Iotro { res: Resolution, g: Group } impl Iotro { fn new(res: Resolution) -> Self { Self { res, g: Group::new() .with_fill("white") .with_text_anchor("middle") .with_dominant_baseline("hanging") .with_font_family("Noto Sans") } } fn add_text>( &mut self, font_size: FontSize, font_weight: FontWeight, y_top: usize, content: T ) { let mut text = Text::new() .with_x(960) .with_y(y_top) .with_font_size(font_size as u16) .with_font_weight(font_weight as u16); text.push(content.into()); self.g.push(text); } fn finish(self) -> Graphic { let mut svg = Graphic::new(); svg.set_width(self.res.width()); svg.set_height(self.res.height()); svg.set_view_box("0 0 1920 1080"); svg.push( Rect::new() .with_fill("black") .with_x(0) .with_y(0) .with_width(1920) .with_height(1080) ); svg.push(self.g); svg } } pub(crate) fn intro(res: Resolution, lecture: &ProjectLecture) -> Graphic { use self::{FontSize::*, FontWeight::*}; let lang = &lecture.lang; let mut intro = Iotro::new(res); intro.add_text(Huge, Bold, 110, &lecture.label); intro.add_text(Huge, SemiBold, 250, &lecture.docent); intro.add_text( Huge, SemiBold, 460, format!( "{} {}", lang.lecture_from, (lang.format_date_long)(lecture.date) ) ); intro.add_text(Big, Normal, 870, lang.video_created_by_us); intro.add_text(Big, Normal, 930, "https://video.fsmpi.rwth-aachen.de"); intro.add_text(Big, Normal, 990, "video@fsmpi.rwth-aachen.de"); intro.finish() } pub(crate) fn outro(lang: &Language<'_>, res: Resolution) -> Graphic { use self::{FontSize::*, FontWeight::*}; let mut outro = Iotro::new(res); outro.add_text(Large, SemiBold, 50, lang.video_created_by); outro.add_text(Huge, Bold, 210, "Video AG, Fachschaft I/1"); outro.add_text(Large, Normal, 360, format!("{}:", lang.our_website)); outro.add_text(Large, Normal, 430, "https://www.fsmpi.rwth-aachen.de"); outro.add_text(Large, Normal, 570, format!("{}:", lang.download_videos)); outro.add_text(Large, Normal, 640, "https://video.fsmpi.rwth-aachen.de"); outro.add_text(Large, Normal, 780, format!("{}:", lang.questions_feedback)); outro.add_text(Large, Normal, 850, "video@fsmpi.rwth-aachen.de"); outro.finish() }