From 8643d47338273c85ff9ae2c8bd8452fffc190535 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 1 Jan 2026 23:14:08 -0800 Subject: [PATCH] port Font to Rust --- src/main.rs | 512 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) diff --git a/src/main.rs b/src/main.rs index 5d7e01c..73c6bb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,517 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use non_nan_float::NonNaNF32; + mod quad_tree; +mod non_nan_float { + #[derive(Default, PartialEq, PartialOrd, Clone, Copy)] + pub(crate) struct NonNaNF32(f32); + + impl std::fmt::Debug for NonNaNF32 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + impl std::fmt::Display for NonNaNF32 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + impl NonNaNF32 { + pub(crate) const fn new(v: f32) -> Option { + if v.is_nan() { None } else { Some(Self(v)) } + } + pub(crate) const fn get(self) -> f32 { + self.0 + } + } + + impl std::hash::Hash for NonNaNF32 { + fn hash(&self, state: &mut H) { + if self.0 == 0.0 { 0.0 } else { self.0 } + .to_bits() + .hash(state); + } + } + + impl Eq for NonNaNF32 {} + + impl Ord for NonNaNF32 { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.partial_cmp(other).expect("known to be non-NaN") + } + } +} + +macro_rules! make_enum_font { + ( + enum $Font:ident { + #[other] + $Other:ident $other_body:tt, + $(#[group] + $KnownFontGroup:ident { + $(#[name = $known_font_name:literal, size = $known_font_size:literal] + $KnownFont:ident,)* + },)* + } + ) => { + #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] + enum $Font { + $Other $other_body, + $($($KnownFont,)*)* + } + + #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)] + enum KnownFontGroup { + $($KnownFontGroup,)* + } + + impl KnownFontGroup { + const fn fonts(self) -> &'static [Font] { + match self { + $(Self::$KnownFontGroup => &[$(Font::$KnownFont,)*],)* + } + } + const INSN_CODE_FONT_GROUPS: &[Self] = &[Self::InsnCode, Self::InsnCodeSubscript]; + } + + impl $Font { + const fn size(&self) -> f32 { + match *self { + Self::$Other { size, .. } => size.get(), + $($(Self::$KnownFont => $known_font_size,)*)* + } + } + const fn font_name(&self) -> &str { + match self { + Self::$Other { font_name, .. } => font_name, + $($(Self::$KnownFont => $known_font_name,)*)* + } + } + const fn known_font_group(&self) -> Option { + match self { + Self::$Other { .. } => None, + $($(Self::$KnownFont => Some(KnownFontGroup::$KnownFontGroup),)*)* + } + } + const fn line_height(&self) -> f32 { + match self { + Self::$Other { .. } => self.line_height_helper(), + $($(Self::$KnownFont => const { Self::$KnownFont.line_height_helper() },)*)* + } + } + } + }; +} + +make_enum_font! { + enum Font { + #[other] + Other { + font_name: Box, + size: NonNaNF32, + }, + #[group] + InsnHeader { + #[name = "YDJYQV+DejaVuSansCondensed-BoldOblique", size = 9.963] + InsnHeader, + }, + #[group] + RtlFnHeader { + #[name = "APUYSQ+zcoN-Regular", size = 9.963] + RtlFnHeader, + }, + #[group] + PageHeader { + #[name = "MJBFWM+DejaVuSansCondensed", size = 9.963] + PageHeader, + }, + #[group] + PageFooter { + #[name = "MJBFWM+DejaVuSansCondensed", size = 4.981] + PageFooter, + }, + #[group] + InsnDesc { + #[name = "MJBFWM+DejaVuSansCondensed", size = 8.966] + InsnDesc0, + #[name = "FZTIYT+CMMI9", size = 8.966] + InsnDesc1, + #[name = "ONUAYC+CMSSI9", size = 8.966] + InsnDesc2, + #[name = "TNGBFZ+CMSY9", size = 8.966] + InsnDesc3, + #[name = "WHMZPU+CMEX9", size = 8.966] + InsnDesc4, + #[name = "ZJTMSG+CMSS9", size = 8.966] + InsnDesc5, + }, + #[group] + InsnDescMisc { + #[name = "MJBFWM+DejaVuSansCondensed", size = 2.377] + InsnDescMisc0, + #[name = "MJBFWM+DejaVuSansCondensed", size = 2.561] + InsnDescMisc1, + #[name = "MJBFWM+DejaVuSansCondensed", size = 4.492] + InsnDescMisc2, + #[name = "MJBFWM+DejaVuSansCondensed", size = 4.641] + InsnDescMisc3, + #[name = "MJBFWM+DejaVuSansCondensed", size = 4.772] + InsnDescMisc4, + #[name = "MJBFWM+DejaVuSansCondensed", size = 4.864] + InsnDescMisc5, + #[name = "MJBFWM+DejaVuSansCondensed", size = 4.925] + InsnDescMisc6, + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.097] + InsnDescMisc7, + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.123] + InsnDescMisc8, + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.131] + InsnDescMisc9, + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.516] + InsnDescMisc10, + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.604] + InsnDescMisc11, + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.634] + InsnDescMisc12, + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.906] + InsnDescMisc13, + #[name = "MJBFWM+DejaVuSansCondensed", size = 6.033] + InsnDescMisc14, + #[name = "MJBFWM+DejaVuSansCondensed", size = 6.068] + InsnDescMisc15, + #[name = "MJBFWM+DejaVuSansCondensed", size = 6.213] + InsnDescMisc16, + #[name = "MJBFWM+DejaVuSansCondensed", size = 6.252] + InsnDescMisc17, + #[name = "MJBFWM+DejaVuSansCondensed", size = 6.962] + InsnDescMisc18, + #[name = "MJBFWM+DejaVuSansCondensed", size = 7.977] + InsnDescMisc19, + }, + #[group] + InsnDescCode { + #[name = "APUYSQ+zcoN-Regular", size = 6.974] + InsnDescCode, + }, + #[group] + InsnDescCodeMisc { + #[name = "APUYSQ+zcoN-Regular", size = 3.587] + InsnDescCodeMisc0, + #[name = "APUYSQ+zcoN-Regular", size = 4.483] + InsnDescCodeMisc1, + }, + #[group] + InsnDescItalic { + #[name = "CGMSHV+DejaVuSansCondensed-Oblique", size = 8.966] + InsnDescItalic, + }, + #[group] + InsnDescBold { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 8.966] + InsnDescBold, + }, + #[group] + InsnDescBoldItalic { + #[name = "YDJYQV+DejaVuSansCondensed-BoldOblique", size = 8.966] + InsnDescBoldItalic, + }, + #[group] + InsnDescSmall { + #[name = "MJBFWM+DejaVuSansCondensed", size = 7.97] + InsnDescSmall, + }, + #[group] + InsnDescSmallItalic { + #[name = "CGMSHV+DejaVuSansCondensed-Oblique", size = 7.97] + InsnDescSmallItalic, + }, + #[group] + InsnDescSmallBold { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 7.97] + InsnDescSmallBold, + }, + #[group] + InsnDescSmallBoldItalic { + #[name = "YDJYQV+DejaVuSansCondensed-BoldOblique", size = 7.97] + InsnDescSmallBoldItalic, + }, + #[group] + InsnDescBoldMisc { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 2.21] + InsnDescBoldMisc0, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 2.399] + InsnDescBoldMisc1, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 2.763] + InsnDescBoldMisc2, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 2.946] + InsnDescBoldMisc3, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 2.949] + InsnDescBoldMisc4, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 2.999] + InsnDescBoldMisc5, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.065] + InsnDescBoldMisc6, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.086] + InsnDescBoldMisc7, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.183] + InsnDescBoldMisc8, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.686] + InsnDescBoldMisc9, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.744] + InsnDescBoldMisc10, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.825] + InsnDescBoldMisc11, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.842] + InsnDescBoldMisc12, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.857] + InsnDescBoldMisc13, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 3.979] + InsnDescBoldMisc14, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.032] + InsnDescBoldMisc15, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.112] + InsnDescBoldMisc16, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.161] + InsnDescBoldMisc17, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.206] + InsnDescBoldMisc18, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.353] + InsnDescBoldMisc19, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.378] + InsnDescBoldMisc20, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.434] + InsnDescBoldMisc21, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.595] + InsnDescBoldMisc22, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.619] + InsnDescBoldMisc23, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.647] + InsnDescBoldMisc24, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.68] + InsnDescBoldMisc25, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.693] + InsnDescBoldMisc26, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.736] + InsnDescBoldMisc27, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.781] + InsnDescBoldMisc28, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.802] + InsnDescBoldMisc29, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 4.995] + InsnDescBoldMisc30, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.201] + InsnDescBoldMisc31, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.258] + InsnDescBoldMisc32, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.363] + InsnDescBoldMisc33, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.442] + InsnDescBoldMisc34, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.473] + InsnDescBoldMisc35, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.485] + InsnDescBoldMisc36, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.512] + InsnDescBoldMisc37, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.543] + InsnDescBoldMisc38, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.613] + InsnDescBoldMisc39, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.744] + InsnDescBoldMisc40, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.774] + InsnDescBoldMisc41, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.809] + InsnDescBoldMisc42, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.849] + InsnDescBoldMisc43, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.911] + InsnDescBoldMisc44, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.92] + InsnDescBoldMisc45, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.962] + InsnDescBoldMisc46, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.981] + InsnDescBoldMisc47, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.146] + InsnDescBoldMisc48, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.213] + InsnDescBoldMisc49, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.221] + InsnDescBoldMisc50, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.243] + InsnDescBoldMisc51, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.55] + InsnDescBoldMisc52, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.62] + InsnDescBoldMisc53, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.699] + InsnDescBoldMisc54, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.725] + InsnDescBoldMisc55, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.751] + InsnDescBoldMisc56, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.856] + InsnDescBoldMisc57, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 8.029] + InsnDescBoldMisc58, + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 8.406] + InsnDescBoldMisc59, + }, + #[group] + InsnDescSubscript { + #[name = "MJBFWM+DejaVuSansCondensed", size = 5.978] + InsnDescSubscript, + }, + #[group] + InsnDescBoldSubscript { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 5.978] + InsnDescBoldSubscript, + }, + #[group] + InsnDescItalicSubscript { + #[name = "CGMSHV+DejaVuSansCondensed-Oblique", size = 5.978] + InsnDescItalicSubscript, + }, + #[group] + InsnDescBoldItalicSubscript { + #[name = "YDJYQV+DejaVuSansCondensed-BoldOblique", size = 5.978] + InsnDescBoldItalicSubscript, + }, + #[group] + InsnExtMnemonic { + #[name = "APUYSQ+zcoN-Regular", size = 8.966] + InsnExtMnemonic, + }, + #[group] + InsnCode { + #[name = "APUYSQ+zcoN-Regular", size = 7.97] + InsnCode0, + #[name = "RRFUNA+CMSY8", size = 7.97] + InsnCode1, + #[name = "HPXOZC+CMSS8", size = 7.97] + InsnCode2, + }, + #[group] + InsnCodeSubscript { + #[name = "APUYSQ+zcoN-Regular", size = 5.978] + InsnCodeSubscript0, + #[name = "DBQTKF+CMSY6", size = 5.978] + InsnCodeSubscript1, + }, + #[group] + TitlePageBig { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 24.787] + TitlePageBig, + }, + #[group] + TitlePageVersion { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 9.963] + TitlePageVersion, + }, + #[group] + TitlePageTm { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 6.974] + TitlePageTm, + }, + #[group] + TitlePageRev { + #[name = "MJBFWM+DejaVuSansCondensed", size = 6.974] + TitlePageRev, + }, + #[group] + TitlePageBook { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 20.663] + TitlePageBook, + }, + #[group] + LegalPageItalic { + #[name = "CGMSHV+DejaVuSansCondensed-Oblique", size = 9.963] + LegalPageItalic, + }, + #[group] + ChangeSummaryPageBold { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 11.955] + ChangeSummaryPageBold, + }, + #[group] + ChapterTitle { + #[name = "NHUPPK+DejaVuSansCondensed-Bold", size = 17.215] + ChapterTitle, + }, + #[group] + MathMisc { + #[name = "AAJMKT+CMMI6", size = 5.978] + MathMisc0, + #[name = "CUTMFD+CMSSI8", size = 5.978] + MathMisc1, + #[name = "CUTMFD+CMSSI8", size = 7.97] + MathMisc2, + #[name = "FZTIYT+CMMI9", size = 5.734] + MathMisc3, + #[name = "FZTIYT+CMMI9", size = 7.168] + MathMisc4, + #[name = "HONFQS+CMMI8", size = 7.97] + MathMisc5, + #[name = "HPXOZC+CMSS8", size = 5.978] + MathMisc6, + #[name = "LLVRDD+CMSY10", size = 11.955] + MathMisc7, + #[name = "ZJTMSG+CMSS9", size = 7.168] + MathMisc8, + }, + } +} + +impl Font { + const fn space_width(&self) -> f32 { + self.size() * const { 3.985 / Font::InsnCode0.size() } + } + const fn line_height_helper(&self) -> f32 { + const fn str_eq(a: &str, b: &str) -> bool { + let a = a.as_bytes(); + let b = b.as_bytes(); + if a.len() != b.len() { + return false; + } + let mut i = 0; + while i < a.len() { + if a[i] != b[i] { + return false; + } + i += 1; + } + true + } + let font_name = self.font_name(); + let mut i = 0; + while i < KnownFontGroup::INSN_CODE_FONT_GROUPS.len() { + let fonts = KnownFontGroup::INSN_CODE_FONT_GROUPS[i].fonts(); + let mut j = 0; + while j < fonts.len() { + if str_eq(font_name, fonts[j].font_name()) { + return 9.464 * self.size() / Font::InsnCode0.size(); + } + j += 1; + } + i += 1; + } + let group = self.known_font_group(); + if matches!(group, Some(KnownFontGroup::InsnDesc)) + || str_eq(font_name, Font::InsnDesc0.font_name()) + || str_eq(font_name, Font::InsnDescBold.font_name()) + || str_eq(font_name, Font::InsnDescItalic.font_name()) + || str_eq(font_name, Font::InsnDescBoldItalic.font_name()) + || matches!(group, Some(KnownFontGroup::MathMisc)) + { + return 10.959 * self.size() / Font::InsnDesc0.size(); + } + panic!("no line height") + } +} + fn main() {}