1#![allow(dead_code)]
5
6extern crate alloc;
7use alloc::collections::BTreeMap;
8use alloc::string::String;
9use alloc::vec::Vec;
10
11struct HeapBuf<T>(Vec<T>);
33
34impl<T> Default for HeapBuf<T> {
35 fn default() -> Self {
36 Self(Vec::new())
37 }
38}
39
40impl<T> alloc_no_stdlib::SliceWrapper<T> for HeapBuf<T> {
41 fn slice(&self) -> &[T] {
42 &self.0
43 }
44}
45
46impl<T> alloc_no_stdlib::SliceWrapperMut<T> for HeapBuf<T> {
47 fn slice_mut(&mut self) -> &mut [T] {
48 &mut self.0
49 }
50}
51
52struct HeapAllocator;
53
54impl<T: Clone + Default> alloc_no_stdlib::Allocator<T> for HeapAllocator {
55 type AllocatedMemory = HeapBuf<T>;
56
57 fn alloc_cell(&mut self, len: usize) -> HeapBuf<T> {
59 HeapBuf(alloc::vec![T::default(); len])
60 }
61
62 fn free_cell(&mut self, _data: HeapBuf<T>) {}
64}
65
66pub fn decompress_brotli(compressed: &[u8], decompressed_size: usize) -> Option<Vec<u8>> {
72 let mut s = brotli_decompressor::BrotliState::new(
73 HeapAllocator,
74 HeapAllocator,
75 HeapAllocator,
76 );
77
78 let mut out_vec = alloc::vec![0u8; decompressed_size.max(16)];
82
83 let mut available_in: usize = compressed.len();
84 let mut input_offset: usize = 0;
85 let mut available_out: usize = out_vec.len();
86 let mut output_offset: usize = 0;
87 let mut total_out: usize = 0;
88
89 let result = brotli_decompressor::BrotliDecompressStream(
90 &mut available_in,
91 &mut input_offset,
92 compressed,
93 &mut available_out,
94 &mut output_offset,
95 &mut out_vec,
96 &mut total_out,
97 &mut s,
98 );
99
100 if matches!(result, brotli_decompressor::BrotliResult::ResultSuccess) {
101 out_vec.truncate(total_out);
102 Some(out_vec)
103 } else {
104 crate::warn!(
105 "[FONT][BR] Brotli 展開に失敗 result={:?} in={} total_out={} in_left={}",
106 result,
107 compressed.len(),
108 total_out,
109 available_in
110 );
111 None
112 }
113}
114
115static WOFF2_KNOWN_TAGS: [&[u8; 4]; 63] = [
116 b"cmap", b"head", b"hhea", b"hmtx", b"maxp", b"name", b"OS/2", b"post",
117 b"cvt ", b"fpgm", b"glyf", b"loca", b"prep", b"CFF ", b"VORG", b"EBDT",
118 b"EBLC", b"gasp", b"hdmx", b"kern", b"LTSH", b"PCLT", b"VDMX", b"vhea",
119 b"vmtx", b"BASE", b"GDEF", b"GPOS", b"GSUB", b"JSTF", b"DSIG", b"MATH",
120 b"CBDT", b"CBLC", b"COLR", b"CPAL", b"SVG ", b"sbix", b"acnt", b"avar",
121 b"bdat", b"bloc", b"bsln", b"cvar", b"fdsc", b"fmtx", b"fvar", b"gvar",
122 b"hsty", b"just", b"lcar", b"mort", b"morx", b"opbd", b"prop", b"trak",
123 b"Zapf", b"Silf", b"Gloc", b"Glat", b"Feat", b"Sill", b"SVG ",
124];
125
126fn read_woff2_uint16(data: &[u8], offset: &mut usize) -> Option<u16> {
127 if *offset + 2 > data.len() {
128 return None;
129 }
130 let val = u16::from_be_bytes([data[*offset], data[*offset + 1]]);
131 *offset += 2;
132 Some(val)
133}
134
135fn read_woff2_uint32(data: &[u8], offset: &mut usize) -> Option<u32> {
136 if *offset + 4 > data.len() {
137 return None;
138 }
139 let val = u32::from_be_bytes([
140 data[*offset],
141 data[*offset + 1],
142 data[*offset + 2],
143 data[*offset + 3],
144 ]);
145 *offset += 4;
146 Some(val)
147}
148
149fn read_woff2_base128(data: &[u8], offset: &mut usize) -> Option<u32> {
150 let mut accum: u32 = 0;
151 for _ in 0..5 {
152 if *offset >= data.len() {
153 return None;
154 }
155 let code = data[*offset];
156 *offset += 1;
157 if accum & 0xFE00_0000 != 0 {
158 return None;
159 }
160 accum = (accum << 7) | ((code & 0x7F) as u32);
161 if (code & 0x80) == 0 {
162 return Some(accum);
163 }
164 }
165 None
166}
167
168struct Woff2Table {
169 tag: [u8; 4],
170 orig_length: u32,
171 transform_length: Option<u32>,
172 flags: u8,
173}
174
175pub fn decompress_woff2(bytes: &[u8]) -> Option<Vec<u8>> {
177 if bytes.len() < 48 || &bytes[0..4] != b"wOF2" {
178 return None;
179 }
180 let mut offset = 4;
181 let flavor = read_woff2_uint32(bytes, &mut offset)?;
182 let _length = read_woff2_uint32(bytes, &mut offset)?;
183 let num_tables = read_woff2_uint16(bytes, &mut offset)? as usize;
184 let _reserved = read_woff2_uint16(bytes, &mut offset)?;
185 let total_sfnt_size = read_woff2_uint32(bytes, &mut offset)? as usize;
186 let total_compressed_size = read_woff2_uint32(bytes, &mut offset)? as usize;
187 let _major_version = read_woff2_uint16(bytes, &mut offset)?;
188 let _minor_version = read_woff2_uint16(bytes, &mut offset)?;
189 let _meta_offset = read_woff2_uint32(bytes, &mut offset)?;
190 let _meta_length = read_woff2_uint32(bytes, &mut offset)?;
191 let _meta_orig_length = read_woff2_uint32(bytes, &mut offset)?;
192 let _priv_offset = read_woff2_uint32(bytes, &mut offset)?;
193 let _priv_length = read_woff2_uint32(bytes, &mut offset)?;
194
195 let mut tables = Vec::with_capacity(num_tables);
196 let mut total_uncompressed_stream_len: usize = 0;
197
198 for _ in 0..num_tables {
199 if offset >= bytes.len() {
200 return None;
201 }
202 let flags = bytes[offset];
203 offset += 1;
204 let tag_idx = flags & 0x3F;
205
206 let tag = if tag_idx == 63 {
207 if offset + 4 > bytes.len() {
208 return None;
209 }
210 let t = [bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]];
211 offset += 4;
212 t
213 } else if (tag_idx as usize) < WOFF2_KNOWN_TAGS.len() {
214 *WOFF2_KNOWN_TAGS[tag_idx as usize]
215 } else {
216 return None;
217 };
218
219 let orig_length = read_woff2_base128(bytes, &mut offset)?;
220 let transform_version = (flags >> 6) & 0x03;
221
222 let is_glyf_or_loca = &tag == b"glyf" || &tag == b"loca";
240 let transformed = if is_glyf_or_loca {
241 transform_version == 0
242 } else {
243 transform_version != 0
244 };
245
246 let transform_length = if transformed {
247 let tl = read_woff2_base128(bytes, &mut offset)?;
248 total_uncompressed_stream_len += tl as usize;
249 Some(tl)
250 } else {
251 total_uncompressed_stream_len += orig_length as usize;
252 None
253 };
254
255 tables.push(Woff2Table {
256 tag,
257 orig_length,
258 transform_length,
259 flags,
260 });
261 }
262
263 if offset + total_compressed_size > bytes.len() {
264 return None;
265 }
266
267 let compressed_data = &bytes[offset..offset + total_compressed_size];
268 crate::warn!(
271 "[WOFF2] decompress compressed={} expected_uncompressed={}",
272 compressed_data.len(),
273 total_uncompressed_stream_len
274 );
275 let decompressed_stream = decompress_brotli(compressed_data, total_uncompressed_stream_len)?;
276
277 let mut sfnt = Vec::with_capacity(total_sfnt_size.max(12 + num_tables * 16));
279
280 sfnt.extend_from_slice(&flavor.to_be_bytes());
282 sfnt.extend_from_slice(&(num_tables as u16).to_be_bytes());
283
284 let mut max_p2 = 1u16;
285 while max_p2 * 2 <= (num_tables as u16) {
286 max_p2 *= 2;
287 }
288 let search_range = max_p2 * 16;
289 let entry_selector = (16 - max_p2.leading_zeros() - 1) as u16;
290 let range_shift = (num_tables as u16) * 16 - search_range;
291
292 sfnt.extend_from_slice(&search_range.to_be_bytes());
293 sfnt.extend_from_slice(&entry_selector.to_be_bytes());
294 sfnt.extend_from_slice(&range_shift.to_be_bytes());
295
296 let table_dir_offset = 12;
298 let data_start_offset = table_dir_offset + num_tables * 16;
299
300 let mut sorted_indices: Vec<usize> = (0..num_tables).collect();
301 sorted_indices.sort_by_key(|&idx| tables[idx].tag);
302
303 sfnt.resize(data_start_offset, 0);
305
306 let mut stream_offset = 0usize;
317 let mut table_ranges: Vec<(usize, usize)> = Vec::with_capacity(num_tables);
318 for table in tables.iter().take(num_tables) {
319 let len = table.orig_length as usize;
320 let read_len = table.transform_length.map(|l| l as usize).unwrap_or(len);
321 if stream_offset + read_len > decompressed_stream.len() {
322 crate::warn!(
323 "[FONT][WOFF2] 展開ストリームが短い tag={:?} 必要={} 残り={}",
324 core::str::from_utf8(&table.tag).unwrap_or("????"),
325 read_len,
326 decompressed_stream.len().saturating_sub(stream_offset)
327 );
328 return None;
329 }
330 table_ranges.push((stream_offset, stream_offset + read_len));
331 stream_offset += read_len;
332 }
333
334 let mut rebuilt: alloc::collections::BTreeMap<usize, Vec<u8>> =
343 alloc::collections::BTreeMap::new();
344 {
345 let glyf_idx = tables.iter().take(num_tables).position(|t| {
346 &t.tag == b"glyf" && t.transform_length.is_some()
347 });
348 if let Some(gi) = glyf_idx {
349 let (rs, re) = table_ranges[gi];
350 let raw = decompressed_stream.get(rs..re)?;
351 match crate::kernel::woff2_glyf::reconstruct_glyf(raw) {
352 Some((glyf_data, loca_data, _fmt)) => {
353 crate::warn!(
354 "[FONT][WOFF2] glyf 変換を復元 {} -> {} バイト(loca {} バイト)",
355 raw.len(),
356 glyf_data.len(),
357 loca_data.len()
358 );
359 rebuilt.insert(gi, glyf_data);
360 if let Some(li) = tables
361 .iter()
362 .take(num_tables)
363 .position(|t| &t.tag == b"loca")
364 {
365 rebuilt.insert(li, loca_data);
366 }
367 }
368 None => {
369 crate::warn!(
370 "[FONT][WOFF2] glyf 変換の復元に失敗({} バイト)",
371 raw.len()
372 );
373 return None;
374 }
375 }
376 }
377 }
378
379 let mut current_data_offset = data_start_offset;
380
381 for &idx in &sorted_indices {
382 let table = &tables[idx];
383 let len = table.orig_length as usize;
384 let (rs, re) = table_ranges[idx];
385 let table_raw: &[u8] = match rebuilt.get(&idx) {
387 Some(v) => v.as_slice(),
388 None => &decompressed_stream[rs..re],
389 };
390 let len = if rebuilt.contains_key(&idx) {
391 table_raw.len()
392 } else {
393 len
394 };
395
396 let mut sum: u32 = 0;
398 let mut i = 0;
399 while i < table_raw.len() {
400 let b0 = table_raw[i] as u32;
401 let b1 = table_raw.get(i + 1).copied().unwrap_or(0) as u32;
402 let b2 = table_raw.get(i + 2).copied().unwrap_or(0) as u32;
403 let b3 = table_raw.get(i + 3).copied().unwrap_or(0) as u32;
404 sum = sum.wrapping_add((b0 << 24) | (b1 << 16) | (b2 << 8) | b3);
405 i += 4;
406 }
407
408 let dir_entry_pos = table_dir_offset + sorted_indices.iter().position(|&x| x == idx)? * 16;
409 sfnt[dir_entry_pos..dir_entry_pos + 4].copy_from_slice(&table.tag);
410 sfnt[dir_entry_pos + 4..dir_entry_pos + 8].copy_from_slice(&sum.to_be_bytes());
411 sfnt[dir_entry_pos + 8..dir_entry_pos + 12].copy_from_slice(&(current_data_offset as u32).to_be_bytes());
412 sfnt[dir_entry_pos + 12..dir_entry_pos + 16].copy_from_slice(&(len as u32).to_be_bytes());
413
414 sfnt.extend_from_slice(table_raw);
415 let padding = (4 - (table_raw.len() % 4)) % 4;
416 sfnt.extend(core::iter::repeat_n(0u8, padding));
417 current_data_offset += table_raw.len() + padding;
418 }
419
420 Some(sfnt)
421}
422
423use spin::Mutex;
424use ttf_parser::{Face, OutlineBuilder};
425
426fn f32_floor(x: f32) -> i32 {
427 if x >= 0.0 {
428 x as i32
429 } else {
430 (x - 1.0) as i32
431 }
432}
433
434fn f32_ceil(x: f32) -> i32 {
435 if x >= 0.0 {
436 (x + 1.0) as i32
437 } else {
438 x as i32
439 }
440}
441
442#[derive(Debug, Clone, Copy, PartialEq)]
443struct Point {
444 x: f32,
445 y: f32,
446}
447
448#[derive(Debug, Clone)]
449enum Segment {
450 Line(Point, Point),
451}
452
453struct GlyphOutlineBuilder {
455 segments: Vec<Segment>,
456 current: Point,
457 start: Point,
458 scale: f32,
459}
460
461impl GlyphOutlineBuilder {
462 fn new(scale: f32) -> Self {
463 Self {
464 segments: Vec::new(),
465 current: Point { x: 0.0, y: 0.0 },
466 start: Point { x: 0.0, y: 0.0 },
467 scale,
468 }
469 }
470
471 fn flatten_quad(&mut self, p0: Point, p1: Point, p2: Point) {
473 let steps = 6; let mut prev = p0;
475 for i in 1..=steps {
476 let t = (i as f32) / (steps as f32);
477 let mt = 1.0 - t;
478 let x = mt * mt * p0.x + 2.0 * mt * t * p1.x + t * t * p2.x;
480 let y = mt * mt * p0.y + 2.0 * mt * t * p1.y + t * t * p2.y;
481 let current = Point { x, y };
482 self.segments.push(Segment::Line(prev, current));
483 prev = current;
484 }
485 }
486}
487
488impl OutlineBuilder for GlyphOutlineBuilder {
489 fn move_to(&mut self, x: f32, y: f32) {
490 let p = Point {
491 x: x * self.scale,
492 y: y * self.scale,
493 };
494 self.current = p;
495 self.start = p;
496 }
497
498 fn line_to(&mut self, x: f32, y: f32) {
499 let p = Point {
500 x: x * self.scale,
501 y: y * self.scale,
502 };
503 self.segments.push(Segment::Line(self.current, p));
504 self.current = p;
505 }
506
507 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
508 let p1 = Point {
509 x: x1 * self.scale,
510 y: y1 * self.scale,
511 };
512 let p2 = Point {
513 x: x * self.scale,
514 y: y * self.scale,
515 };
516 self.flatten_quad(self.current, p1, p2);
517 self.current = p2;
518 }
519
520 fn curve_to(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, x: f32, y: f32) {
521 let p2 = Point {
524 x: x * self.scale,
525 y: y * self.scale,
526 };
527 self.segments.push(Segment::Line(self.current, p2));
528 self.current = p2;
529 }
530
531 fn close(&mut self) {
532 if self.current != self.start {
533 self.segments.push(Segment::Line(self.current, self.start));
534 }
535 }
536}
537
538#[derive(Clone)]
540pub struct CachedGlyph {
541 pub width: u32,
542 pub height: u32,
543 pub x_offset: i32,
544 pub y_offset: i32,
545 pub advance: u32,
546 pub data: Vec<u8>, }
548
549#[derive(Clone)]
551pub struct CachedColorGlyph {
552 pub width: u32,
553 pub height: u32,
554 pub x_offset: i32,
555 pub y_offset: i32,
556 pub advance: u32,
557 pub data: Vec<u32>, }
559
560pub struct VectorFont {
562 face: Face<'static>,
563 fallbacks: Vec<Face<'static>>,
565 cache: BTreeMap<(char, u32), CachedGlyph>, color_cache: BTreeMap<(char, u32, u32, u32), CachedColorGlyph>, advance_cache: BTreeMap<(char, u32), u32>, }
569
570fn fallback_glyph_char(c: char) -> Option<char> {
573 let sub = match c {
574 '␣' => '_', '⇥' => '>', '↵' => '<', '⇠' => '<', '–' | '—' | '―' | '‐' | '‑' | '−' => '-',
581 '\u{2018}' | '\u{2019}' | '‚' | '‛' | '`' => '\'',
583 '\u{201C}' | '\u{201D}' | '„' | '‟' => '"',
584 '•' | '◦' | '‣' | '·' | '∙' => '*',
586 '×' => 'x',
588 '÷' => '/',
589 '…' => '.',
591 '\u{00A0}' | '\u{202F}' | '\u{2009}' | '\u{200A}' => ' ',
593 '\u{f060}' => '\u{2190}', '\u{f061}' => '\u{2192}', '\u{f062}' => '\u{2191}', '\u{f063}' => '\u{2193}', '\u{f053}' => '\u{2039}', '\u{f054}' => '\u{203A}', '\u{f077}' => '^', '\u{f078}' => 'v', '\u{f104}' => '\u{2039}', '\u{f105}' => '\u{203A}', '\u{f106}' => '^', '\u{f107}' => 'v', '\u{f00c}' => '\u{2713}', '\u{f00d}' => '\u{2715}', '\u{f05a}' => 'i', '\u{f35d}' => '↗', '\u{f10d}' => '"', '\u{f10e}' => '"', _ => return None,
643 };
644 Some(sub)
645}
646
647impl VectorFont {
648 pub fn new(font_bytes: &'static [u8]) -> Result<Self, ttf_parser::FaceParsingError> {
649 let face = Face::parse(font_bytes, 0)?;
650 Ok(Self {
651 face,
652 fallbacks: Vec::new(),
653 cache: BTreeMap::new(),
654 color_cache: BTreeMap::new(),
655 advance_cache: BTreeMap::new(),
656 })
657 }
658
659 pub fn new_with_fallbacks(
663 font_bytes: &'static [u8],
664 fallback_bytes: &[&'static [u8]],
665 ) -> Result<Self, ttf_parser::FaceParsingError> {
666 let face = Face::parse(font_bytes, 0)?;
667 let mut fallbacks = Vec::new();
668 for fb in fallback_bytes {
669 if let Ok(f) = Face::parse(fb, 0) {
670 fallbacks.push(f);
671 }
672 }
673 Ok(Self {
674 face,
675 fallbacks,
676 cache: BTreeMap::new(),
677 color_cache: BTreeMap::new(),
678 advance_cache: BTreeMap::new(),
679 })
680 }
681
682 pub fn set_primary(&mut self, font_bytes: &'static [u8]) -> Result<(), ttf_parser::FaceParsingError> {
688 let new_face = Face::parse(font_bytes, 0)?;
689 let old_primary = core::mem::replace(&mut self.face, new_face);
690 self.fallbacks.insert(0, old_primary);
691 self.cache.clear();
692 self.color_cache.clear();
693 self.advance_cache.clear();
694 Ok(())
695 }
696
697 pub fn primary_has_glyph(&self, c: char) -> bool {
708 self.face.glyph_index(c).is_some()
709 }
710
711 pub fn has_glyph(&self, c: char) -> bool {
712 if ('\u{e000}'..='\u{f8ff}').contains(&c) {
717 return self.face.glyph_index(c).is_some();
718 }
719 if self.face.glyph_index(c).is_some() {
720 return true;
721 }
722 self.fallbacks.iter().any(|f| f.glyph_index(c).is_some())
723 }
724
725 pub fn get_glyph(&mut self, c: char, size_px: u32) -> Option<&CachedGlyph> {
726 let key = (c, size_px);
727 if self.cache.contains_key(&key) {
728 return self.cache.get(&key);
729 }
730
731 if self.cache.len() >= 8000 {
732 crate::info!(
733 "[vector_font] Cache limit (8000) reached. Clearing glyph cache to free memory."
734 );
735 self.cache.clear();
736 }
737
738 let (face_idx, glyph_id) = {
741 let find = |ch: char| -> Option<(usize, ttf_parser::GlyphId)> {
742 if let Some(id) = self.face.glyph_index(ch) {
743 return Some((0, id));
744 }
745 if ('\u{e000}'..='\u{f8ff}').contains(&ch) {
758 return None;
759 }
760 for (i, f) in self.fallbacks.iter().enumerate() {
761 if let Some(id) = f.glyph_index(ch) {
762 return Some((i + 1, id));
763 }
764 }
765 None
766 };
767 match find(c) {
768 Some(x) => x,
769 None => {
770 fallback_glyph_char(c).and_then(find)?
772 }
773 }
774 };
775 let face: &Face = if face_idx == 0 {
776 &self.face
777 } else {
778 &self.fallbacks[face_idx - 1]
779 };
780
781 let units_per_em = face.units_per_em() as f32;
782 let scale = (size_px as f32) / units_per_em;
783
784 let advance = (face.glyph_hor_advance(glyph_id)? as f32 * scale) as u32;
786
787 let mut builder = GlyphOutlineBuilder::new(scale);
790 face.outline_glyph(glyph_id, &mut builder);
791
792 if builder.segments.is_empty() {
795 let empty_glyph = CachedGlyph {
796 width: 0,
797 height: 0,
798 x_offset: 0,
799 y_offset: 0,
800 advance,
801 data: Vec::new(),
802 };
803 self.cache.insert(key, empty_glyph);
804 return self.cache.get(&key);
805 }
806
807 let bounding_box = face.glyph_bounding_box(glyph_id)?;
809
810 let scale_y = -scale;
812
813 let mut segments = builder.segments;
815 for seg in &mut segments {
816 match seg {
817 Segment::Line(p1, p2) => {
818 p1.y = -p1.y;
819 p2.y = -p2.y;
820 }
821 }
822 }
823
824 let bx0 = bounding_box.x_min as f32 * scale;
826 let by0 = bounding_box.y_max as f32 * scale_y; let bx1 = bounding_box.x_max as f32 * scale;
828 let by1 = bounding_box.y_min as f32 * scale_y; let gx0 = f32_floor(bx0) - 1;
831 let gy0 = f32_floor(by0) - 1;
832 let gx1 = f32_ceil(bx1) + 1;
833 let gy1 = f32_ceil(by1) + 1;
834
835 let width = (gx1 - gx0).max(1) as u32;
836 let height = (gy1 - gy0).max(1) as u32;
837 let x_offset = gx0;
838 let y_offset = gy0;
839
840 if width > 500 || height > 500 || width == 0 || height == 0 {
841 crate::warn!(
842 "[NET][DIAG] glyph c={:?} units_per_em={} scale={} bbox=({},{},{},{}) w={} h={} face_idx={}",
843 c,
844 units_per_em,
845 scale,
846 bounding_box.x_min,
847 bounding_box.y_min,
848 bounding_box.x_max,
849 bounding_box.y_max,
850 width,
851 height,
852 face_idx
853 );
854 }
855
856 let mut data = alloc::vec![0u8; (width * height) as usize];
861 let mut coverage = alloc::vec![0u8; width as usize];
862 let mut xs: alloc::vec::Vec<f32> = alloc::vec::Vec::new();
863
864 for py in 0..height {
865 let screen_y = (gy0 + py as i32) as f32;
866 for c in coverage.iter_mut() {
867 *c = 0;
868 }
869
870 for sy in 0..4 {
872 let sub_y = screen_y + (sy as f32 + 0.5) / 4.0;
873
874 xs.clear();
876 for seg in &segments {
877 match seg {
878 Segment::Line(p1, p2) => {
879 let y_min = p1.y.min(p2.y);
880 let y_max = p1.y.max(p2.y);
881 if sub_y >= y_min && sub_y < y_max && p1.y != p2.y {
882 let t = (sub_y - p1.y) / (p2.y - p1.y);
883 xs.push(p1.x + t * (p2.x - p1.x));
884 }
885 }
886 }
887 }
888 if xs.is_empty() {
889 continue;
890 }
891 xs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal));
893
894 let mut i = 0;
897 while i + 1 < xs.len() {
898 let span_start = xs[i];
899 let span_end = xs[i + 1];
900 i += 2;
901 if span_end <= span_start {
902 continue;
903 }
904 let px_lo = (f32_floor(span_start) - gx0 - 1).max(0) as u32;
908 let px_hi = ((f32_ceil(span_end) - gx0 + 1).max(0) as u32).min(width);
909 for px in px_lo..px_hi {
910 let screen_x = (gx0 + px as i32) as f32;
911 for sx in 0..4 {
912 let sub_x = screen_x + (sx as f32 + 0.5) / 4.0;
913 if sub_x >= span_start && sub_x < span_end {
914 coverage[px as usize] += 1;
915 }
916 }
917 }
918 }
919 }
920
921 for px in 0..width {
922 let alpha = ((coverage[px as usize] as u32 * 255) / 16).min(255) as u8;
923 data[(py * width + px) as usize] = alpha;
924 }
925 }
926
927 let cached = CachedGlyph {
928 width,
929 height,
930 x_offset,
931 y_offset,
932 advance,
933 data,
934 };
935
936 self.cache.insert(key, cached);
937 self.cache.get(&key)
938 }
939
940 pub fn get_color_glyph(
942 &mut self,
943 c: char,
944 size_px: u32,
945 fg_color: u32,
946 bg_color: u32,
947 ) -> Option<&CachedColorGlyph> {
948 let key = (c, size_px, fg_color, bg_color);
949 if self.color_cache.contains_key(&key) {
950 return self.color_cache.get(&key);
951 }
952
953 if self.color_cache.len() >= 16000 {
954 self.color_cache.clear();
955 }
956
957 let glyph = self.get_glyph(c, size_px)?.clone();
958
959 let mut color_data = Vec::with_capacity(glyph.data.len());
960 let fg_r = (fg_color >> 16) & 0xFF;
961 let fg_g = (fg_color >> 8) & 0xFF;
962 let fg_b = fg_color & 0xFF;
963 let bg_r = (bg_color >> 16) & 0xFF;
964 let bg_g = (bg_color >> 8) & 0xFF;
965 let bg_b = bg_color & 0xFF;
966
967 for &alpha in &glyph.data {
968 if alpha == 0 {
969 color_data.push(0x00000000); } else if alpha == 255 {
971 color_data.push(fg_color | 0xFF000000); } else {
973 let a = alpha as u32;
974 let inv_a = 255 - a;
975 let r = ((fg_r * a + bg_r * inv_a) / 255) & 0xFF;
976 let g = ((fg_g * a + bg_g * inv_a) / 255) & 0xFF;
977 let b = ((fg_b * a + bg_b * inv_a) / 255) & 0xFF;
978 color_data.push(0xFF000000 | (r << 16) | (g << 8) | b);
979 }
980 }
981
982 let cached = CachedColorGlyph {
983 width: glyph.width,
984 height: glyph.height,
985 x_offset: glyph.x_offset,
986 y_offset: glyph.y_offset,
987 advance: glyph.advance,
988 data: color_data,
989 };
990
991 self.color_cache.insert(key, cached);
992 self.color_cache.get(&key)
993 }
994
995 pub fn get_glyph_advance(&mut self, c: char, size_px: u32) -> u32 {
998 let key = (c, size_px);
999 if let Some(glyph) = self.cache.get(&key) {
1000 return glyph.advance;
1001 }
1002 if let Some(&adv) = self.advance_cache.get(&key) {
1003 return adv;
1004 }
1005
1006 let (face_idx, glyph_id) = {
1007 let find = |ch: char| -> Option<(usize, ttf_parser::GlyphId)> {
1008 if let Some(id) = self.face.glyph_index(ch) {
1009 return Some((0, id));
1010 }
1011 if ('\u{e000}'..='\u{f8ff}').contains(&ch) {
1024 return None;
1025 }
1026 for (i, f) in self.fallbacks.iter().enumerate() {
1027 if let Some(id) = f.glyph_index(ch) {
1028 return Some((i + 1, id));
1029 }
1030 }
1031 None
1032 };
1033 match find(c) {
1034 Some(x) => x,
1035 None => {
1036 if let Some(alt) = fallback_glyph_char(c).and_then(find) {
1037 alt
1038 } else {
1039 let default_adv = size_px / 2;
1040 self.advance_cache.insert(key, default_adv);
1041 return default_adv;
1042 }
1043 }
1044 }
1045 };
1046
1047 let face: &Face = if face_idx == 0 {
1048 &self.face
1049 } else {
1050 &self.fallbacks[face_idx - 1]
1051 };
1052 let units_per_em = face.units_per_em() as f32;
1053 let scale = (size_px as f32) / units_per_em;
1054 let advance = if let Some(adv) = face.glyph_hor_advance(glyph_id) {
1055 (adv as f32 * scale) as u32
1056 } else {
1057 size_px / 2
1058 };
1059
1060 if self.advance_cache.len() >= 10000 {
1061 self.advance_cache.clear();
1062 }
1063 self.advance_cache.insert(key, advance);
1064 advance
1065 }
1066
1067 pub fn get_string_width(&mut self, s: &str, size_px: u32) -> u32 {
1069 self.get_string_width_ls(s, size_px, 0)
1070 }
1071
1072 pub fn get_string_width_ls(&mut self, s: &str, size_px: u32, ls: i32) -> u32 {
1074 self.get_string_width_ls_ws(s, size_px, ls, 0)
1075 }
1076
1077 pub fn get_string_width_ls_ws(&mut self, s: &str, size_px: u32, ls: i32, ws: i32) -> u32 {
1079 let mut width = 0i32;
1080 for c in s.chars() {
1081 width += self.get_glyph_advance(c, size_px) as i32 + ls + if c == ' ' { ws } else { 0 };
1082 }
1083 width.max(0) as u32
1084 }
1085
1086 pub fn get_string_wrapped_size(&mut self, s: &str, size_px: u32, max_width: u32) -> (u32, u32) {
1094 self.get_string_wrapped_size_ls_ws(s, size_px, max_width, 0, 0)
1095 }
1096
1097 pub fn get_string_wrapped_size_ls(
1099 &mut self,
1100 s: &str,
1101 size_px: u32,
1102 max_width: u32,
1103 ls: i32,
1104 ) -> (u32, u32) {
1105 self.get_string_wrapped_size_ls_ws(s, size_px, max_width, ls, 0)
1106 }
1107
1108 pub fn get_string_wrapped_size_ls_ws(
1109 &mut self,
1110 s: &str,
1111 size_px: u32,
1112 max_width: u32,
1113 ls: i32,
1114 ws: i32,
1115 ) -> (u32, u32) {
1116 self.get_string_wrapped_size_ext(s, size_px, max_width, ls, ws, false, false)
1117 }
1118
1119 pub fn get_string_wrapped_size_ext(
1121 &mut self,
1122 s: &str,
1123 size_px: u32,
1124 max_width: u32,
1125 ls: i32,
1126 ws: i32,
1127 word_break_all: bool,
1128 white_space_pre_wrap: bool,
1129 ) -> (u32, u32) {
1130 let mut max_w = 0u32;
1131 let mut curr_w = 0u32;
1132 let mut emitted_lines = 0u32;
1135 let mut seg_has_content = false;
1136 let line_height = size_px + 4;
1137
1138 let mut prev_char: Option<char> = None;
1139 let mut prev_advance = 0u32;
1140
1141 for c in s.chars() {
1142 if c == '\n' {
1143 if white_space_pre_wrap {
1144 if curr_w > max_w {
1146 max_w = curr_w;
1147 }
1148 emitted_lines += 1;
1149 curr_w = 0;
1150 seg_has_content = false;
1151 prev_char = None;
1152 prev_advance = 0;
1153 continue;
1154 } else {
1155 if curr_w > max_w {
1158 max_w = curr_w;
1159 }
1160 emitted_lines += 1;
1161 curr_w = 0;
1162 seg_has_content = false;
1163 prev_char = None;
1164 prev_advance = 0;
1165 continue;
1166 }
1167 }
1168
1169 let extra = ls + if c == ' ' { ws } else { 0 };
1171 let advance = (self.get_glyph_advance(c, size_px) as i32 + extra).max(0) as u32;
1172
1173 if curr_w > 0 && curr_w + advance > max_width {
1174 if !word_break_all && is_line_start_forbidden(c) {
1177 curr_w += advance;
1178 seg_has_content = true;
1179 prev_char = Some(c);
1180 prev_advance = advance;
1181 continue;
1182 }
1183
1184 let can_break_here = prev_char
1193 .map(|pc| {
1194 crate::os_lib::layout::line_break::can_break_between(
1195 pc,
1196 c,
1197 word_break_all,
1198 )
1199 })
1200 .unwrap_or(true);
1201 if !can_break_here {
1202 curr_w += advance;
1210 seg_has_content = true;
1211 prev_char = Some(c);
1212 prev_advance = advance;
1213 continue;
1214 }
1215
1216 let send_to_next = !word_break_all && prev_char.map(is_line_end_forbidden).unwrap_or(false);
1219 let line_w = if send_to_next {
1220 curr_w.saturating_sub(prev_advance)
1221 } else {
1222 curr_w
1223 };
1224 if line_w > max_w {
1225 max_w = line_w;
1226 }
1227
1228 emitted_lines += 1;
1229
1230 curr_w = if send_to_next { prev_advance } else { 0 } + advance;
1232 seg_has_content = true;
1233 prev_char = Some(c);
1234 prev_advance = advance;
1235 continue;
1236 }
1237
1238 curr_w += advance;
1239 seg_has_content = true;
1240 prev_char = Some(c);
1241 prev_advance = advance;
1242 }
1243
1244 if curr_w > max_w {
1245 max_w = curr_w;
1246 }
1247 if seg_has_content {
1248 emitted_lines += 1;
1249 }
1250
1251 (max_w, emitted_lines.max(1) * line_height)
1252 }
1253}
1254
1255pub static GLOBAL_VECTOR_FONT: Mutex<Option<VectorFont>> = Mutex::new(None);
1257pub static GLOBAL_TERMINAL_FONT: Mutex<Option<VectorFont>> = Mutex::new(None);
1258
1259pub static CUSTOM_FONTS: Mutex<BTreeMap<String, VectorFont>> = Mutex::new(BTreeMap::new());
1276
1277fn looks_like_ttf_or_otf(bytes: &[u8]) -> bool {
1279 if bytes.len() < 4 {
1280 return false;
1281 }
1282 matches!(
1283 &bytes[0..4],
1284 [0x00, 0x01, 0x00, 0x00] | [b'O', b'T', b'T', b'O'] | [b't', b'r', b'u', b'e']
1285 )
1286}
1287
1288fn looks_like_woff2(bytes: &[u8]) -> bool {
1309 bytes.len() >= 4 && &bytes[0..4] == b"wOF2"
1310}
1311
1312pub fn font_key(family: &str, weight: u16) -> alloc::string::String {
1324 alloc::format!("{}|{}", family.trim().to_lowercase(), weight)
1325}
1326
1327pub fn register_custom_font_weighted(family: &str, weight: u16, bytes: Vec<u8>) -> bool {
1329 register_custom_font_inner(family, weight, bytes)
1330}
1331
1332pub fn register_custom_font(family: &str, bytes: Vec<u8>) -> bool {
1333 register_custom_font_inner(family, 400, bytes)
1334}
1335
1336fn register_custom_font_inner(family: &str, weight: u16, bytes: Vec<u8>) -> bool {
1337 crate::warn!(
1354 "[FONT] 登録開始 family={} bytes={}",
1355 family,
1356 bytes.len()
1357 );
1358 let fallback_chain: Vec<&'static [u8]> = DEFAULT_FONT_CHAIN
1362 .iter()
1363 .filter_map(|n| font_bytes(n))
1364 .collect();
1365 let font_bytes = if looks_like_woff2(&bytes) {
1366 match decompress_woff2(&bytes) {
1367 Some(sfnt) => sfnt,
1368 None => {
1369 crate::warn!(
1370 "[FONT] WOFF2 の展開に失敗 family={} bytes={}",
1371 family,
1372 bytes.len()
1373 );
1374 return false;
1375 }
1376 }
1377 } else if looks_like_ttf_or_otf(&bytes) {
1378 bytes
1379 } else {
1380 crate::warn!(
1381 "[FONT] 未知のフォント形式 family={} bytes={} head={:02x?}",
1382 family,
1383 bytes.len(),
1384 &bytes[..bytes.len().min(4)]
1385 );
1386 return false;
1387 };
1388
1389 let leaked: &'static [u8] = Vec::leak(font_bytes);
1390 let _pf = crate::os_lib::web_engine::perf::start();
1391 crate::warn!("[FONT] 解析開始 family={} bytes={}", family, leaked.len());
1392 let _parsed = VectorFont::new_with_fallbacks(leaked, &fallback_chain);
1402 crate::os_lib::web_engine::perf::add(
1403 crate::os_lib::web_engine::perf::Slot::FontParse,
1404 _pf,
1405 );
1406 match _parsed {
1407 Ok(font) => {
1408 {
1409 let mut map = CUSTOM_FONTS.lock();
1410 map.insert(font_key(family, weight), font);
1412 let plain = family.trim().to_lowercase();
1416 if !map.contains_key(&plain) {
1417 if let Ok(f2) = VectorFont::new_with_fallbacks(leaked, &fallback_chain) {
1418 map.insert(plain, f2);
1419 }
1420 }
1421 }
1422 let probe = |c: char| -> bool {
1446 CUSTOM_FONTS
1447 .lock()
1448 .get(&family.trim().to_lowercase())
1449 .map(|f| f.has_glyph(c))
1450 .unwrap_or(false)
1451 };
1452 crate::warn!(
1453 "[FONT] 登録完了 family={} bytes={} 字形F02D={} 字形F015={}",
1454 family,
1455 leaked.len(),
1456 probe('\u{f02d}'),
1457 probe('\u{f015}')
1458 );
1459 true
1460 }
1461 Err(e) => {
1462 crate::warn!(
1463 "[FONT] フォントのパースに失敗 family={} bytes={} err={:?}",
1464 family,
1465 leaked.len(),
1466 e
1467 );
1468 false
1469 }
1470 }
1471}
1472
1473pub fn has_custom_font_weighted(family: &str, weight: u16) -> bool {
1483 CUSTOM_FONTS.lock().contains_key(&font_key(family, weight))
1484}
1485
1486pub fn has_custom_font(family: &str) -> bool {
1487 CUSTOM_FONTS.lock().contains_key(&family.trim().to_lowercase())
1488}
1489
1490pub const AVAILABLE_FONTS: &[&str] = &[
1498 "Mplus1Code-Regular",
1499 "HackGenConsoleNF-Regular",
1500 "ipam",
1501 "ipag",
1502 "MPLUS2-Regular",
1503 "MPLUS2-Bold",
1504];
1505
1506pub const DEFAULT_FONT_CHAIN: [&str; 3] =
1508 ["Mplus1Code-Regular", "HackGenConsoleNF-Regular", "ipam"];
1509
1510pub fn font_bytes(name: &str) -> Option<&'static [u8]> {
1512 Some(match name {
1513 "Mplus1Code-Regular" => include_bytes!("../../fonts/Mplus1Code/Mplus1Code-Regular.ttf"),
1514 "HackGenConsoleNF-Regular" => {
1515 include_bytes!("../../fonts/HackGen/HackGenConsoleNF-Regular.ttf")
1516 }
1517 "ipam" => include_bytes!("../../fonts/ipa/ipam.ttf"),
1518 "ipag" | "IPAg" => include_bytes!("../../fonts/ipa/ipag.ttf"),
1519 "MPLUS2-Regular" => include_bytes!("../../fonts/MPLUS2/MPLUS2-Regular.ttf"),
1520 "MPLUS2-Bold" => include_bytes!("../../fonts/MPLUS2/MPLUS2-Bold.ttf"),
1521 _ => return None,
1522 })
1523}
1524
1525pub fn init_global_font_chain(names: &[&str]) {
1529 let mut bytes: Vec<&'static [u8]> = names.iter().filter_map(|n| font_bytes(n)).collect();
1530 if bytes.is_empty() {
1531 bytes = DEFAULT_FONT_CHAIN
1532 .iter()
1533 .filter_map(|n| font_bytes(n))
1534 .collect();
1535 }
1536 if bytes.is_empty() {
1537 crate::info!("[vector_font] No embedded fonts available for chain init!");
1538 return;
1539 }
1540 let primary = bytes[0];
1541 let fallbacks: Vec<&'static [u8]> = bytes[1..].to_vec();
1542 match VectorFont::new_with_fallbacks(primary, &fallbacks) {
1543 Ok(font) => {
1544 crate::info!(
1545 "[vector_font] Global font chain initialized ({} fonts).",
1546 bytes.len()
1547 );
1548 *GLOBAL_VECTOR_FONT.lock() = Some(font);
1549 }
1550 Err(e) => {
1551 crate::info!("[vector_font] Failed to init font chain: {:?}", e);
1552 }
1553 }
1554}
1555
1556pub fn init_global_font(font_bytes: &'static [u8]) {
1558 match VectorFont::new(font_bytes) {
1559 Ok(mut font) => {
1560 crate::info!("[vector_font] Successfully initialized global vector font.");
1561 crate::info!("[vector_font] Running test get_glyph('o', 16) for global font...");
1562 if font.get_glyph('o', 16).is_some() {
1563 crate::info!("[vector_font] Test get_glyph('o', 16) succeeded for global font!");
1564 } else {
1565 crate::info!("[vector_font] Test get_glyph('o', 16) failed for global font!");
1566 }
1567 *GLOBAL_VECTOR_FONT.lock() = Some(font);
1568 }
1569 Err(e) => {
1570 crate::info!(
1571 "[vector_font] Failed to initialize global vector font: {:?}",
1572 e
1573 );
1574 }
1575 }
1576}
1577
1578pub fn init_terminal_font() {
1580 let font_bytes = include_bytes!("../../fonts/Mplus1Code/Mplus1Code-Regular.ttf");
1581 match VectorFont::new(font_bytes) {
1582 Ok(mut font) => {
1583 crate::info!("[vector_font] Successfully initialized terminal monospace font.");
1584 crate::info!("[vector_font] Running test get_glyph('o', 16)...");
1585 if font.get_glyph('o', 16).is_some() {
1586 crate::info!("[vector_font] Test get_glyph('o', 16) succeeded!");
1587 } else {
1588 crate::info!("[vector_font] Test get_glyph('o', 16) failed!");
1589 }
1590 *GLOBAL_TERMINAL_FONT.lock() = Some(font);
1591 }
1592 Err(e) => {
1593 crate::info!(
1594 "[vector_font] Failed to initialize terminal monospace font: {:?}",
1595 e
1596 );
1597 }
1598 }
1599}
1600
1601pub fn get_vector_string_width(s: &str, size_px: u32) -> u32 {
1603 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1604 font.get_string_width(s, size_px)
1605 } else {
1606 s.len() as u32 * (size_px / 2) }
1608}
1609
1610pub fn get_vector_string_width_ls(s: &str, size_px: u32, ls: i32) -> u32 {
1612 get_vector_string_width_ls_ws(s, size_px, ls, 0)
1613}
1614
1615pub fn get_vector_string_width_ls_ws(s: &str, size_px: u32, ls: i32, ws: i32) -> u32 {
1617 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1618 font.get_string_width_ls_ws(s, size_px, ls, ws)
1619 } else {
1620 let space_count = s.chars().filter(|&c| c == ' ').count() as i32;
1621 let base = s.len() as i32 * (size_px / 2) as i32
1622 + s.chars().count() as i32 * ls
1623 + space_count * ws;
1624 base.max(0) as u32
1625 }
1626}
1627
1628pub fn get_vector_char_width(c: char, size_px: u32) -> u32 {
1631 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1632 font.get_glyph_advance(c, size_px)
1633 } else {
1634 size_px / 2
1635 }
1636}
1637
1638pub fn get_vector_string_wrapped_size(s: &str, size_px: u32, max_width: u32) -> (u32, u32) {
1639 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1640 font.get_string_wrapped_size(s, size_px, max_width)
1641 } else {
1642 let char_w = size_px / 2;
1643 let max_chars = (max_width / char_w).max(1);
1644 let chars_count = s.chars().count() as u32;
1645 let lines = chars_count.div_ceil(max_chars);
1646 (
1647 max_width.min(chars_count * char_w),
1648 lines.max(1) * (size_px + 4),
1649 )
1650 }
1651}
1652
1653pub fn get_vector_string_wrapped_size_ls(
1655 s: &str,
1656 size_px: u32,
1657 max_width: u32,
1658 ls: i32,
1659) -> (u32, u32) {
1660 get_vector_string_wrapped_size_ls_ws(s, size_px, max_width, ls, 0)
1661}
1662
1663pub fn get_vector_string_wrapped_size_ls_ws(
1665 s: &str,
1666 size_px: u32,
1667 max_width: u32,
1668 ls: i32,
1669 ws: i32,
1670) -> (u32, u32) {
1671 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1672 font.get_string_wrapped_size_ls_ws(s, size_px, max_width, ls, ws)
1673 } else {
1674 let char_w = ((size_px / 2) as i32 + ls).max(1) as u32;
1675 let max_chars = (max_width / char_w).max(1);
1676 let chars_count = s.chars().count() as u32;
1677 let lines = chars_count.div_ceil(max_chars);
1678 (
1679 max_width.min(chars_count * char_w),
1680 lines.max(1) * (size_px + 4),
1681 )
1682 }
1683}
1684
1685pub fn get_vector_string_wrapped_size_ext(
1686 s: &str,
1687 size_px: u32,
1688 max_width: u32,
1689 ls: i32,
1690 ws: i32,
1691 word_break_all: bool,
1692 white_space_pre_wrap: bool,
1693) -> (u32, u32) {
1694 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1695 font.get_string_wrapped_size_ext(s, size_px, max_width, ls, ws, word_break_all, white_space_pre_wrap)
1696 } else {
1697 let char_w = ((size_px / 2) as i32 + ls).max(1) as u32;
1698 let max_chars = (max_width / char_w).max(1);
1699 let chars_count = s.chars().count() as u32;
1700 let lines = chars_count.div_ceil(max_chars);
1701 (
1702 max_width.min(chars_count * char_w),
1703 lines.max(1) * (size_px + 4),
1704 )
1705 }
1706}
1707
1708pub fn is_line_start_forbidden(c: char) -> bool {
1709 matches!(
1710 c,
1711 '、' | '。'
1712 | ','
1713 | '.'
1714 | '・'
1715 | ':'
1716 | ';'
1717 | '?'
1718 | '!'
1719 | ')'
1720 | ']'
1721 | '}'
1722 | '〉'
1723 | '》'
1724 | '』'
1725 | '」'
1726 | '〕'
1727 | 'ー'
1728 | '〜'
1729 | '々'
1730 | '〆'
1731 | 'ぁ'
1732 | 'ぃ'
1733 | 'ぅ'
1734 | 'ぇ'
1735 | 'ぉ'
1736 | 'っ'
1737 | 'ゃ'
1738 | 'ゅ'
1739 | 'ょ'
1740 | 'ゎ'
1741 | 'ァ'
1742 | 'ィ'
1743 | 'ゥ'
1744 | 'ェ'
1745 | 'ォ'
1746 | 'ッ'
1747 | 'ャ'
1748 | 'ュ'
1749 | 'ョ'
1750 | 'ヮ'
1751 | 'ヶ'
1752 | 'ヵ'
1753 )
1754}
1755
1756pub fn is_line_end_forbidden(c: char) -> bool {
1757 matches!(c, '(' | '[' | '{' | '〈' | '《' | '『' | '「' | '〔')
1758}