2023-10-28 21:38:17 +00:00
|
|
|
//! A module for writing intros and outros
|
|
|
|
|
2024-05-17 19:29:22 +00:00
|
|
|
use crate::{time::Date, ProjectLecture, Resolution};
|
|
|
|
use anyhow::anyhow;
|
|
|
|
use std::{
|
|
|
|
fmt::{self, Debug, Display, Formatter},
|
|
|
|
str::FromStr
|
|
|
|
};
|
2023-10-28 21:38:17 +00:00
|
|
|
use svgwriter::{
|
2024-05-25 11:53:15 +00:00
|
|
|
tags::{Group, Rect, TagWithPresentationAttributes as _, Text},
|
2023-10-28 21:38:17 +00:00
|
|
|
Graphic
|
|
|
|
};
|
|
|
|
|
2024-05-17 19:29:22 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Language<'a> {
|
2024-05-24 10:17:40 +00:00
|
|
|
pub(crate) lang: &'a str,
|
|
|
|
pub(crate) format_date_long: fn(Date) -> String,
|
2024-05-17 19:29:22 +00:00
|
|
|
// intro
|
|
|
|
lecture_from: &'a str,
|
2024-05-24 10:17:40 +00:00
|
|
|
pub(crate) video_created_by_us: &'a str,
|
2024-05-17 19:29:22 +00:00
|
|
|
// outro
|
|
|
|
video_created_by: &'a str,
|
|
|
|
our_website: &'a str,
|
|
|
|
download_videos: &'a str,
|
2024-05-24 10:17:40 +00:00
|
|
|
questions_feedback: &'a str,
|
|
|
|
// metadata
|
|
|
|
pub(crate) from: &'a str
|
2024-05-17 19:29:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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",
|
2024-05-24 10:17:40 +00:00
|
|
|
questions_feedback: "Fragen, Vorschläge und Feedback",
|
|
|
|
|
|
|
|
from: "vom"
|
2024-05-17 19:29:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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",
|
2024-05-24 10:17:40 +00:00
|
|
|
|
2024-05-17 19:29:22 +00:00
|
|
|
video_created_by: "Video created by the",
|
|
|
|
our_website: "The Fachschaft's website",
|
|
|
|
download_videos: "Download videos",
|
2024-05-24 10:17:40 +00:00
|
|
|
questions_feedback: "Questions, Suggestions and Feedback",
|
|
|
|
|
|
|
|
from: "from"
|
2024-05-17 19:29:22 +00:00
|
|
|
};
|
|
|
|
|
2024-05-18 13:21:14 +00:00
|
|
|
impl Default for Language<'static> {
|
|
|
|
fn default() -> Self {
|
|
|
|
GERMAN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-17 19:29:22 +00:00
|
|
|
impl FromStr for Language<'static> {
|
|
|
|
type Err = anyhow::Error;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-28 21:38:17 +00:00
|
|
|
#[repr(u16)]
|
2024-05-25 11:53:15 +00:00
|
|
|
pub(crate) enum FontSize {
|
2023-10-28 21:38:17 +00:00
|
|
|
Huge = 72,
|
|
|
|
Large = 56,
|
|
|
|
Big = 44
|
|
|
|
}
|
|
|
|
|
|
|
|
#[repr(u16)]
|
2024-05-25 11:53:15 +00:00
|
|
|
pub(crate) enum FontWeight {
|
2023-10-28 21:38:17 +00:00
|
|
|
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<T: Into<String>>(
|
|
|
|
&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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-10 10:55:00 +00:00
|
|
|
pub(crate) fn intro(res: Resolution, lecture: &ProjectLecture) -> Graphic {
|
2023-10-28 21:38:17 +00:00
|
|
|
use self::{FontSize::*, FontWeight::*};
|
|
|
|
|
2024-05-17 19:29:22 +00:00
|
|
|
let lang = &lecture.lang;
|
2023-10-28 21:38:17 +00:00
|
|
|
let mut intro = Iotro::new(res);
|
2024-04-10 10:55:00 +00:00
|
|
|
intro.add_text(Huge, Bold, 110, &lecture.label);
|
|
|
|
intro.add_text(Huge, SemiBold, 250, &lecture.docent);
|
2023-10-28 21:38:17 +00:00
|
|
|
intro.add_text(
|
|
|
|
Huge,
|
|
|
|
SemiBold,
|
|
|
|
460,
|
2024-05-17 19:29:22 +00:00
|
|
|
format!(
|
|
|
|
"{} {}",
|
|
|
|
lang.lecture_from,
|
|
|
|
(lang.format_date_long)(lecture.date)
|
|
|
|
)
|
2023-10-28 21:38:17 +00:00
|
|
|
);
|
2024-05-17 19:29:22 +00:00
|
|
|
intro.add_text(Big, Normal, 870, lang.video_created_by_us);
|
2023-10-28 21:38:17 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2024-05-17 19:29:22 +00:00
|
|
|
pub(crate) fn outro(lang: &Language<'_>, res: Resolution) -> Graphic {
|
2023-10-28 21:38:17 +00:00
|
|
|
use self::{FontSize::*, FontWeight::*};
|
|
|
|
|
|
|
|
let mut outro = Iotro::new(res);
|
2024-05-17 19:29:22 +00:00
|
|
|
outro.add_text(Large, SemiBold, 50, lang.video_created_by);
|
2023-10-28 21:38:17 +00:00
|
|
|
outro.add_text(Huge, Bold, 210, "Video AG, Fachschaft I/1");
|
2024-05-17 19:29:22 +00:00
|
|
|
outro.add_text(Large, Normal, 360, format!("{}:", lang.our_website));
|
2023-10-28 21:38:17 +00:00
|
|
|
outro.add_text(Large, Normal, 430, "https://www.fsmpi.rwth-aachen.de");
|
2024-05-17 19:29:22 +00:00
|
|
|
outro.add_text(Large, Normal, 570, format!("{}:", lang.download_videos));
|
2023-10-28 21:38:17 +00:00
|
|
|
outro.add_text(Large, Normal, 640, "https://video.fsmpi.rwth-aachen.de");
|
2024-05-17 19:29:22 +00:00
|
|
|
outro.add_text(Large, Normal, 780, format!("{}:", lang.questions_feedback));
|
2023-10-28 21:38:17 +00:00
|
|
|
outro.add_text(Large, Normal, 850, "video@fsmpi.rwth-aachen.de");
|
|
|
|
|
|
|
|
outro.finish()
|
|
|
|
}
|