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 = crate::kernel::font_select::scaled_advance(
787 face.glyph_hor_advance(glyph_id)?,
788 face.units_per_em(),
789 size_px,
790 );
791
792 let mut builder = GlyphOutlineBuilder::new(scale);
795 face.outline_glyph(glyph_id, &mut builder);
796
797 if builder.segments.is_empty() {
800 let empty_glyph = CachedGlyph {
801 width: 0,
802 height: 0,
803 x_offset: 0,
804 y_offset: 0,
805 advance,
806 data: Vec::new(),
807 };
808 self.cache.insert(key, empty_glyph);
809 return self.cache.get(&key);
810 }
811
812 let bounding_box = face.glyph_bounding_box(glyph_id)?;
814
815 let scale_y = -scale;
817
818 let mut segments = builder.segments;
820 for seg in &mut segments {
821 match seg {
822 Segment::Line(p1, p2) => {
823 p1.y = -p1.y;
824 p2.y = -p2.y;
825 }
826 }
827 }
828
829 let bx0 = bounding_box.x_min as f32 * scale;
831 let by0 = bounding_box.y_max as f32 * scale_y; let bx1 = bounding_box.x_max as f32 * scale;
833 let by1 = bounding_box.y_min as f32 * scale_y; let gx0 = f32_floor(bx0) - 1;
836 let gy0 = f32_floor(by0) - 1;
837 let gx1 = f32_ceil(bx1) + 1;
838 let gy1 = f32_ceil(by1) + 1;
839
840 let width = (gx1 - gx0).max(1) as u32;
841 let height = (gy1 - gy0).max(1) as u32;
842 let x_offset = gx0;
843 let y_offset = gy0;
844
845 if width > 500 || height > 500 || width == 0 || height == 0 {
846 crate::warn!(
847 "[NET][DIAG] glyph c={:?} units_per_em={} scale={} bbox=({},{},{},{}) w={} h={} face_idx={}",
848 c,
849 units_per_em,
850 scale,
851 bounding_box.x_min,
852 bounding_box.y_min,
853 bounding_box.x_max,
854 bounding_box.y_max,
855 width,
856 height,
857 face_idx
858 );
859 }
860
861 let mut data = alloc::vec![0u8; (width * height) as usize];
867 let mut coverage = alloc::vec![0u8; width as usize];
868 let mut xs: alloc::vec::Vec<(f32, i8)> = alloc::vec::Vec::new();
869 let mut spans: alloc::vec::Vec<(f32, f32)> = alloc::vec::Vec::new();
870
871 for py in 0..height {
872 let screen_y = (gy0 + py as i32) as f32;
873 for c in coverage.iter_mut() {
874 *c = 0;
875 }
876
877 for sy in 0..4 {
879 let sub_y = screen_y + (sy as f32 + 0.5) / 4.0;
880
881 xs.clear();
883 for seg in &segments {
884 match seg {
885 Segment::Line(p1, p2) => {
886 let y_min = p1.y.min(p2.y);
887 let y_max = p1.y.max(p2.y);
888 if sub_y >= y_min && sub_y < y_max && p1.y != p2.y {
889 let t = (sub_y - p1.y) / (p2.y - p1.y);
890 let dir: i8 = if p2.y > p1.y { 1 } else { -1 };
891 xs.push((p1.x + t * (p2.x - p1.x), dir));
892 }
893 }
894 }
895 }
896 if xs.is_empty() {
897 continue;
898 }
899 crate::kernel::glyph_fill::nonzero_spans(&mut xs, &mut spans);
901
902 for &(span_start, span_end) in spans.iter() {
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(
946 &mut self,
947 c: char,
948 size_px: u32,
949 fg_color: u32,
950 bg_color: u32,
951 ) -> Option<&CachedColorGlyph> {
952 let key = (c, size_px, fg_color, bg_color);
953 if self.color_cache.contains_key(&key) {
954 return self.color_cache.get(&key);
955 }
956
957 if self.color_cache.len() >= 16000 {
958 self.color_cache.clear();
959 }
960
961 let glyph = self.get_glyph(c, size_px)?.clone();
962
963 let mut color_data = Vec::with_capacity(glyph.data.len());
964 let fg_r = (fg_color >> 16) & 0xFF;
965 let fg_g = (fg_color >> 8) & 0xFF;
966 let fg_b = fg_color & 0xFF;
967 let bg_r = (bg_color >> 16) & 0xFF;
968 let bg_g = (bg_color >> 8) & 0xFF;
969 let bg_b = bg_color & 0xFF;
970
971 let from_framebuffer = bg_color == BG_FROM_FRAMEBUFFER;
972 for &alpha in &glyph.data {
973 if alpha == 0 {
974 color_data.push(0x00000000); } else if from_framebuffer && alpha != 255 {
976 color_data.push(((alpha as u32) << 24) | (fg_color & 0x00FF_FFFF));
979 } else if alpha == 255 {
980 color_data.push(fg_color | 0xFF000000); } else {
982 let eff_alpha = crate::os_lib::web_engine::font_gamma::correct_glyph_coverage(
983 alpha,
984 fg_color,
985 bg_color,
986 );
987 let a = eff_alpha as u32;
988 let inv_a = 255 - a;
989 let r = ((fg_r * a + bg_r * inv_a) / 255) & 0xFF;
990 let g = ((fg_g * a + bg_g * inv_a) / 255) & 0xFF;
991 let b = ((fg_b * a + bg_b * inv_a) / 255) & 0xFF;
992 color_data.push(0xFF000000 | (r << 16) | (g << 8) | b);
993 }
994 }
995
996 let cached = CachedColorGlyph {
997 width: glyph.width,
998 height: glyph.height,
999 x_offset: glyph.x_offset,
1000 y_offset: glyph.y_offset,
1001 advance: glyph.advance,
1002 data: color_data,
1003 };
1004
1005 self.color_cache.insert(key, cached);
1006 self.color_cache.get(&key)
1007 }
1008
1009 pub fn get_glyph_advance(&mut self, c: char, size_px: u32) -> u32 {
1012 let key = (c, size_px);
1013 if let Some(glyph) = self.cache.get(&key) {
1014 return glyph.advance;
1015 }
1016 if let Some(&adv) = self.advance_cache.get(&key) {
1017 return adv;
1018 }
1019
1020 let (face_idx, glyph_id) = {
1021 let find = |ch: char| -> Option<(usize, ttf_parser::GlyphId)> {
1022 if let Some(id) = self.face.glyph_index(ch) {
1023 return Some((0, id));
1024 }
1025 if ('\u{e000}'..='\u{f8ff}').contains(&ch) {
1038 return None;
1039 }
1040 for (i, f) in self.fallbacks.iter().enumerate() {
1041 if let Some(id) = f.glyph_index(ch) {
1042 return Some((i + 1, id));
1043 }
1044 }
1045 None
1046 };
1047 match find(c) {
1048 Some(x) => x,
1049 None => {
1050 if let Some(alt) = fallback_glyph_char(c).and_then(find) {
1051 alt
1052 } else {
1053 let default_adv = size_px / 2;
1054 self.advance_cache.insert(key, default_adv);
1055 return default_adv;
1056 }
1057 }
1058 }
1059 };
1060
1061 let face: &Face = if face_idx == 0 {
1062 &self.face
1063 } else {
1064 &self.fallbacks[face_idx - 1]
1065 };
1066 let advance = if let Some(adv) = face.glyph_hor_advance(glyph_id) {
1067 crate::kernel::font_select::scaled_advance(adv, face.units_per_em(), size_px)
1068 } else {
1069 size_px / 2
1070 };
1071
1072 if self.advance_cache.len() >= 10000 {
1073 self.advance_cache.clear();
1074 }
1075 self.advance_cache.insert(key, advance);
1076 advance
1077 }
1078
1079 pub fn get_string_width(&mut self, s: &str, size_px: u32) -> u32 {
1081 self.get_string_width_ls(s, size_px, 0)
1082 }
1083
1084 pub fn get_string_width_ls(&mut self, s: &str, size_px: u32, ls: i32) -> u32 {
1086 self.get_string_width_ls_ws(s, size_px, ls, 0)
1087 }
1088
1089 pub fn get_string_width_ls_ws(&mut self, s: &str, size_px: u32, ls: i32, ws: i32) -> u32 {
1091 let mut width = 0i32;
1092 for c in s.chars() {
1093 width += self.get_glyph_advance(c, size_px) as i32 + ls + if c == ' ' { ws } else { 0 };
1094 }
1095 width.max(0) as u32
1096 }
1097
1098 pub fn get_string_wrapped_size(&mut self, s: &str, size_px: u32, max_width: u32) -> (u32, u32) {
1106 self.get_string_wrapped_size_ls_ws(s, size_px, max_width, 0, 0)
1107 }
1108
1109 pub fn get_string_wrapped_size_ls(
1111 &mut self,
1112 s: &str,
1113 size_px: u32,
1114 max_width: u32,
1115 ls: i32,
1116 ) -> (u32, u32) {
1117 self.get_string_wrapped_size_ls_ws(s, size_px, max_width, ls, 0)
1118 }
1119
1120 pub fn get_string_wrapped_size_ls_ws(
1121 &mut self,
1122 s: &str,
1123 size_px: u32,
1124 max_width: u32,
1125 ls: i32,
1126 ws: i32,
1127 ) -> (u32, u32) {
1128 self.get_string_wrapped_size_ext(s, size_px, max_width, ls, ws, false, false, false)
1129 }
1130
1131 pub fn get_string_wrapped_size_ext(
1133 &mut self,
1134 s: &str,
1135 size_px: u32,
1136 max_width: u32,
1137 ls: i32,
1138 ws: i32,
1139 word_break_all: bool,
1140 overflow_wrap: bool,
1141 _white_space_pre_wrap: bool,
1142 ) -> (u32, u32) {
1143 let line_height = size_px + 4;
1148 let chars: alloc::vec::Vec<char> = s.chars().collect();
1149 let advances: alloc::vec::Vec<i32> = chars
1150 .iter()
1151 .map(|&c| {
1152 if c == '\n' || c == '\u{00AD}' {
1153 0
1154 } else {
1155 let extra = ls + if c == ' ' { ws } else { 0 };
1156 (self.get_glyph_advance(c, size_px) as i32 + extra).max(0)
1157 }
1158 })
1159 .collect();
1160 let breaks = crate::os_lib::layout::line_break::break_positions_ex(
1161 &chars,
1162 &advances,
1163 max_width.min(i32::MAX as u32) as i32,
1164 word_break_all,
1165 overflow_wrap,
1166 |c| !word_break_all && is_line_start_forbidden(c),
1167 |c| !word_break_all && is_line_end_forbidden(c),
1168 );
1169 let mut max_w = 0u32;
1170 let mut emitted_lines = 0u32;
1171 let mut line_w = 0i32;
1172 let mut line_w_no_trailing = 0i32;
1174 let mut seg_has_content = false;
1175 let mut bi = 0usize;
1176 for (i, &c) in chars.iter().enumerate() {
1177 if c == '\n' {
1178 max_w = max_w.max(line_w_no_trailing.max(0) as u32);
1179 emitted_lines += 1;
1180 line_w = 0;
1181 line_w_no_trailing = 0;
1182 seg_has_content = false;
1183 continue;
1184 }
1185 if bi < breaks.len() && breaks[bi] == i {
1186 bi += 1;
1187 max_w = max_w.max(line_w_no_trailing.max(0) as u32);
1188 emitted_lines += 1;
1189 line_w = 0;
1190 line_w_no_trailing = 0;
1191 }
1192 line_w += advances[i];
1193 if !c.is_whitespace() {
1194 line_w_no_trailing = line_w;
1195 }
1196 seg_has_content = true;
1197 }
1198 max_w = max_w.max(line_w_no_trailing.max(0) as u32);
1199 if seg_has_content {
1200 emitted_lines += 1;
1201 }
1202 (max_w, emitted_lines.max(1) * line_height)
1203 }
1204}
1205
1206pub static GLOBAL_VECTOR_FONT: Mutex<Option<VectorFont>> = Mutex::new(None);
1208pub static GLOBAL_TERMINAL_FONT: Mutex<Option<VectorFont>> = Mutex::new(None);
1209
1210pub static CUSTOM_FONTS: Mutex<BTreeMap<String, VectorFont>> = Mutex::new(BTreeMap::new());
1227
1228fn looks_like_ttf_or_otf(bytes: &[u8]) -> bool {
1230 if bytes.len() < 4 {
1231 return false;
1232 }
1233 matches!(
1234 &bytes[0..4],
1235 [0x00, 0x01, 0x00, 0x00] | [b'O', b'T', b'T', b'O'] | [b't', b'r', b'u', b'e']
1236 )
1237}
1238
1239fn looks_like_woff2(bytes: &[u8]) -> bool {
1260 bytes.len() >= 4 && &bytes[0..4] == b"wOF2"
1261}
1262
1263pub const BG_FROM_FRAMEBUFFER: u32 = 0x0100_0000;
1277
1278pub fn font_key(family: &str, weight: u16) -> alloc::string::String {
1284 alloc::format!("{}|{}", family.trim().to_lowercase(), weight)
1285}
1286
1287pub fn register_custom_font_weighted(family: &str, weight: u16, bytes: Vec<u8>) -> bool {
1289 register_custom_font_inner(family, weight, bytes)
1290}
1291
1292pub fn register_custom_font(family: &str, bytes: Vec<u8>) -> bool {
1293 register_custom_font_inner(family, 400, bytes)
1294}
1295
1296static REGISTERED_FONTS: spin::Mutex<Vec<(alloc::string::String, usize, u64)>> = spin::Mutex::new(Vec::new());
1302
1303fn fnv1a64(bytes: &[u8]) -> u64 {
1304 let mut h: u64 = 0xcbf2_9ce4_8422_2325;
1305 for &b in bytes {
1306 h ^= b as u64;
1307 h = h.wrapping_mul(0x0000_0100_0000_01b3);
1308 }
1309 h
1310}
1311
1312fn register_custom_font_inner(family: &str, weight: u16, bytes: Vec<u8>) -> bool {
1313 let fingerprint = (font_key(family, weight), bytes.len(), fnv1a64(&bytes));
1314 if REGISTERED_FONTS.lock().contains(&fingerprint) {
1315 return true;
1316 }
1317 crate::warn!(
1334 "[FONT] 登録開始 family={} weight={} bytes={}",
1335 family,
1336 weight,
1337 bytes.len()
1338 );
1339 let fallback_chain: Vec<&'static [u8]> = DEFAULT_FONT_CHAIN
1343 .iter()
1344 .filter_map(|n| font_bytes(n))
1345 .collect();
1346 let mut font_bytes = if looks_like_woff2(&bytes) {
1347 match decompress_woff2(&bytes) {
1348 Some(sfnt) => sfnt,
1349 None => {
1350 crate::warn!(
1351 "[FONT] WOFF2 の展開に失敗 family={} bytes={}",
1352 family,
1353 bytes.len()
1354 );
1355 return false;
1356 }
1357 }
1358 } else if looks_like_ttf_or_otf(&bytes) {
1359 bytes
1360 } else {
1361 crate::warn!(
1362 "[FONT] 未知のフォント形式 family={} bytes={} head={:02x?}",
1363 family,
1364 bytes.len(),
1365 &bytes[..bytes.len().min(4)]
1366 );
1367 return false;
1368 };
1369
1370 font_bytes.shrink_to_fit();
1371 let leaked: &'static [u8] = Vec::leak(font_bytes);
1372 let _pf = crate::os_lib::web_engine::perf::start();
1373 crate::warn!("[FONT] 解析開始 family={} bytes={}", family, leaked.len());
1374 let _parsed = VectorFont::new_with_fallbacks(leaked, &fallback_chain);
1384 crate::os_lib::web_engine::perf::add(
1385 crate::os_lib::web_engine::perf::Slot::FontParse,
1386 _pf,
1387 );
1388 match _parsed {
1389 Ok(font) => {
1390 REGISTERED_FONTS.lock().push(fingerprint);
1391 {
1392 let mut map = CUSTOM_FONTS.lock();
1393 map.insert(font_key(family, weight), font);
1395 let plain = family.trim().to_lowercase();
1399 if !map.contains_key(&plain) {
1400 if let Ok(f2) = VectorFont::new_with_fallbacks(leaked, &fallback_chain) {
1401 map.insert(plain, f2);
1402 }
1403 }
1404 }
1405 let probe = |c: char| -> bool {
1429 CUSTOM_FONTS
1430 .lock()
1431 .get(&family.trim().to_lowercase())
1432 .map(|f| f.has_glyph(c))
1433 .unwrap_or(false)
1434 };
1435 crate::warn!(
1436 "[FONT] 登録完了 family={} bytes={} 字形F02D={} 字形F015={}",
1437 family,
1438 leaked.len(),
1439 probe('\u{f02d}'),
1440 probe('\u{f015}')
1441 );
1442 true
1443 }
1444 Err(e) => {
1445 crate::warn!(
1446 "[FONT] フォントのパースに失敗 family={} bytes={} err={:?}",
1447 family,
1448 leaked.len(),
1449 e
1450 );
1451 false
1452 }
1453 }
1454}
1455
1456pub fn has_custom_font_weighted(family: &str, weight: u16) -> bool {
1466 CUSTOM_FONTS.lock().contains_key(&font_key(family, weight))
1467}
1468
1469pub fn has_custom_font(family: &str) -> bool {
1470 CUSTOM_FONTS.lock().contains_key(&family.trim().to_lowercase())
1471}
1472
1473pub const AVAILABLE_FONTS: &[&str] = &[
1481 "Mplus1Code-Regular",
1482 "HackGenConsoleNF-Regular",
1483 "ipam",
1484 "ipag",
1485 "MPLUS2-Regular",
1486 "MPLUS2-Bold",
1487];
1488
1489pub const DEFAULT_FONT_CHAIN: [&str; 3] =
1491 ["Mplus1Code-Regular", "HackGenConsoleNF-Regular", "ipam"];
1492
1493pub fn font_bytes(name: &str) -> Option<&'static [u8]> {
1495 Some(match name {
1496 "Mplus1Code-Regular" => include_bytes!("../../fonts/Mplus1Code/Mplus1Code-Regular.ttf"),
1497 "HackGenConsoleNF-Regular" => {
1498 include_bytes!("../../fonts/HackGen/HackGenConsoleNF-Regular.ttf")
1499 }
1500 "ipam" => include_bytes!("../../fonts/ipa/ipam.ttf"),
1501 "ipag" | "IPAg" => include_bytes!("../../fonts/ipa/ipag.ttf"),
1502 "MPLUS2-Regular" => include_bytes!("../../fonts/MPLUS2/MPLUS2-Regular.ttf"),
1503 "MPLUS2-Bold" => include_bytes!("../../fonts/MPLUS2/MPLUS2-Bold.ttf"),
1504 _ => return None,
1505 })
1506}
1507
1508pub fn init_global_font_chain(names: &[&str]) {
1512 let mut bytes: Vec<&'static [u8]> = names.iter().filter_map(|n| font_bytes(n)).collect();
1513 if bytes.is_empty() {
1514 bytes = DEFAULT_FONT_CHAIN
1515 .iter()
1516 .filter_map(|n| font_bytes(n))
1517 .collect();
1518 }
1519 if bytes.is_empty() {
1520 crate::info!("[vector_font] No embedded fonts available for chain init!");
1521 return;
1522 }
1523 let primary = bytes[0];
1524 let fallbacks: Vec<&'static [u8]> = bytes[1..].to_vec();
1525 match VectorFont::new_with_fallbacks(primary, &fallbacks) {
1526 Ok(font) => {
1527 crate::info!(
1528 "[vector_font] Global font chain initialized ({} fonts).",
1529 bytes.len()
1530 );
1531 *GLOBAL_VECTOR_FONT.lock() = Some(font);
1532 }
1533 Err(e) => {
1534 crate::info!("[vector_font] Failed to init font chain: {:?}", e);
1535 }
1536 }
1537}
1538
1539pub fn init_global_font(font_bytes: &'static [u8]) {
1541 match VectorFont::new(font_bytes) {
1542 Ok(mut font) => {
1543 crate::info!("[vector_font] Successfully initialized global vector font.");
1544 crate::info!("[vector_font] Running test get_glyph('o', 16) for global font...");
1545 if font.get_glyph('o', 16).is_some() {
1546 crate::info!("[vector_font] Test get_glyph('o', 16) succeeded for global font!");
1547 } else {
1548 crate::info!("[vector_font] Test get_glyph('o', 16) failed for global font!");
1549 }
1550 *GLOBAL_VECTOR_FONT.lock() = Some(font);
1551 }
1552 Err(e) => {
1553 crate::info!(
1554 "[vector_font] Failed to initialize global vector font: {:?}",
1555 e
1556 );
1557 }
1558 }
1559}
1560
1561pub fn init_terminal_font() {
1563 let font_bytes = include_bytes!("../../fonts/Mplus1Code/Mplus1Code-Regular.ttf");
1564 match VectorFont::new(font_bytes) {
1565 Ok(mut font) => {
1566 crate::info!("[vector_font] Successfully initialized terminal monospace font.");
1567 crate::info!("[vector_font] Running test get_glyph('o', 16)...");
1568 if font.get_glyph('o', 16).is_some() {
1569 crate::info!("[vector_font] Test get_glyph('o', 16) succeeded!");
1570 } else {
1571 crate::info!("[vector_font] Test get_glyph('o', 16) failed!");
1572 }
1573 *GLOBAL_TERMINAL_FONT.lock() = Some(font);
1574 }
1575 Err(e) => {
1576 crate::info!(
1577 "[vector_font] Failed to initialize terminal monospace font: {:?}",
1578 e
1579 );
1580 }
1581 }
1582}
1583
1584pub fn with_family_font<R>(
1589 s: &str,
1590 family_list: &str,
1591 weight: u16,
1592 f: impl FnOnce(&mut VectorFont) -> R,
1593) -> Option<R> {
1594 with_family_font_ext(s, family_list, weight, |font, _| f(font))
1595}
1596
1597pub fn with_family_font_ext<R>(
1600 s: &str,
1601 family_list: &str,
1602 weight: u16,
1603 f: impl FnOnce(&mut VectorFont, u16) -> R,
1604) -> Option<R> {
1605 if family_list.is_empty() {
1606 return None;
1607 }
1608 let mut fonts = CUSTOM_FONTS.lock();
1611 for fam in family_list.split(',') {
1612 let key = fam.trim().trim_matches('"').trim_matches('\'').to_lowercase();
1613 if key.is_empty() {
1614 continue;
1615 }
1616 let prefix = alloc::format!("{}|", key);
1617 let available: Vec<(u16, bool)> = fonts
1618 .range(prefix.clone()..)
1619 .take_while(|(k, _)| k.starts_with(&prefix))
1620 .filter_map(|(k, font)| {
1621 let w = k.get(prefix.len()..)?.parse::<u16>().ok()?;
1622 Some((w, crate::kernel::font_select::covers_all(s, |c| font.primary_has_glyph(c))))
1623 })
1624 .collect();
1625 let picked = crate::kernel::font_select::pick_weight_css(weight, &available);
1626 let hit = picked.map(|w| font_key(&key, w)).unwrap_or(key.clone());
1627 if fonts.contains_key(&hit) {
1628 if let Some(font) = fonts.get_mut(&hit) {
1629 return Some(f(font, picked.unwrap_or(400)));
1630 }
1631 }
1632 for cand in crate::kernel::font_select::generic_family_candidates(&key) {
1635 let prefix = alloc::format!("{}|", cand);
1636 let available: Vec<(u16, bool)> = fonts
1637 .range(prefix.clone()..)
1638 .take_while(|(k, _)| k.starts_with(&prefix))
1639 .filter_map(|(k, font)| {
1640 let w = k.get(prefix.len()..)?.parse::<u16>().ok()?;
1641 Some((w, crate::kernel::font_select::covers_all(s, |c| font.primary_has_glyph(c))))
1642 })
1643 .collect();
1644 if let Some(w) = crate::kernel::font_select::pick_weight_css(weight, &available) {
1645 if let Some(font) = fonts.get_mut(&font_key(cand, w)) {
1646 return Some(f(font, w));
1647 }
1648 }
1649 }
1650 }
1651 None
1652}
1653
1654pub fn get_vector_string_width_family(
1662 s: &str,
1663 size_px: u32,
1664 family_list: &str,
1665 weight: u16,
1666 ls: i32,
1667 ws: i32,
1668) -> u32 {
1669 with_family_font(s, family_list, weight, |f| f.get_string_width_ls_ws(s, size_px, ls, ws))
1670 .unwrap_or_else(|| get_vector_string_width_ls_ws(s, size_px, ls, ws))
1671}
1672
1673#[allow(clippy::too_many_arguments)]
1676pub fn get_vector_string_wrapped_size_family(
1677 s: &str,
1678 size_px: u32,
1679 max_width: u32,
1680 ls: i32,
1681 ws: i32,
1682 word_break_all: bool,
1683 overflow_wrap: bool,
1684 white_space_pre_wrap: bool,
1685 family_list: &str,
1686 weight: u16,
1687) -> (u32, u32) {
1688 with_family_font(s, family_list, weight, |f| {
1689 f.get_string_wrapped_size_ext(s, size_px, max_width, ls, ws, word_break_all, overflow_wrap, white_space_pre_wrap)
1690 })
1691 .unwrap_or_else(|| {
1692 get_vector_string_wrapped_size_ext(s, size_px, max_width, ls, ws, word_break_all, overflow_wrap, white_space_pre_wrap)
1693 })
1694}
1695
1696pub fn get_vector_string_width(s: &str, size_px: u32) -> u32 {
1697 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1698 font.get_string_width(s, size_px)
1699 } else {
1700 s.len() as u32 * (size_px / 2) }
1702}
1703
1704pub fn get_vector_string_width_ls(s: &str, size_px: u32, ls: i32) -> u32 {
1706 get_vector_string_width_ls_ws(s, size_px, ls, 0)
1707}
1708
1709pub fn get_vector_string_width_ls_ws(s: &str, size_px: u32, ls: i32, ws: i32) -> u32 {
1711 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1712 font.get_string_width_ls_ws(s, size_px, ls, ws)
1713 } else {
1714 let space_count = s.chars().filter(|&c| c == ' ').count() as i32;
1715 let base = s.len() as i32 * (size_px / 2) as i32
1716 + s.chars().count() as i32 * ls
1717 + space_count * ws;
1718 base.max(0) as u32
1719 }
1720}
1721
1722pub fn get_vector_char_width(c: char, size_px: u32) -> u32 {
1725 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1726 font.get_glyph_advance(c, size_px)
1727 } else {
1728 size_px / 2
1729 }
1730}
1731
1732pub fn get_vector_string_wrapped_size(s: &str, size_px: u32, max_width: u32) -> (u32, u32) {
1733 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1734 font.get_string_wrapped_size(s, size_px, max_width)
1735 } else {
1736 let char_w = size_px / 2;
1737 let max_chars = (max_width / char_w).max(1);
1738 let chars_count = s.chars().count() as u32;
1739 let lines = chars_count.div_ceil(max_chars);
1740 (
1741 max_width.min(chars_count * char_w),
1742 lines.max(1) * (size_px + 4),
1743 )
1744 }
1745}
1746
1747pub fn get_vector_string_wrapped_size_ls(
1749 s: &str,
1750 size_px: u32,
1751 max_width: u32,
1752 ls: i32,
1753) -> (u32, u32) {
1754 get_vector_string_wrapped_size_ls_ws(s, size_px, max_width, ls, 0)
1755}
1756
1757pub fn get_vector_string_wrapped_size_ls_ws(
1759 s: &str,
1760 size_px: u32,
1761 max_width: u32,
1762 ls: i32,
1763 ws: i32,
1764) -> (u32, u32) {
1765 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1766 font.get_string_wrapped_size_ls_ws(s, size_px, max_width, ls, ws)
1767 } else {
1768 let char_w = ((size_px / 2) as i32 + ls).max(1) as u32;
1769 let max_chars = (max_width / char_w).max(1);
1770 let chars_count = s.chars().count() as u32;
1771 let lines = chars_count.div_ceil(max_chars);
1772 (
1773 max_width.min(chars_count * char_w),
1774 lines.max(1) * (size_px + 4),
1775 )
1776 }
1777}
1778
1779pub fn get_vector_string_wrapped_size_ext(
1780 s: &str,
1781 size_px: u32,
1782 max_width: u32,
1783 ls: i32,
1784 ws: i32,
1785 word_break_all: bool,
1786 overflow_wrap: bool,
1787 white_space_pre_wrap: bool,
1788) -> (u32, u32) {
1789 if let Some(ref mut font) = *GLOBAL_VECTOR_FONT.lock() {
1790 font.get_string_wrapped_size_ext(s, size_px, max_width, ls, ws, word_break_all, overflow_wrap, white_space_pre_wrap)
1791 } else {
1792 let char_w = ((size_px / 2) as i32 + ls).max(1) as u32;
1793 let max_chars = (max_width / char_w).max(1);
1794 let chars_count = s.chars().count() as u32;
1795 let lines = chars_count.div_ceil(max_chars);
1796 (
1797 max_width.min(chars_count * char_w),
1798 lines.max(1) * (size_px + 4),
1799 )
1800 }
1801}
1802
1803pub fn is_line_start_forbidden(c: char) -> bool {
1804 matches!(
1805 c,
1806 '、' | '。'
1807 | ','
1808 | '.'
1809 | '・'
1810 | ':'
1811 | ';'
1812 | '?'
1813 | '!'
1814 | ')'
1815 | ']'
1816 | '}'
1817 | '〉'
1818 | '》'
1819 | '』'
1820 | '」'
1821 | '〕'
1822 | 'ー'
1823 | '〜'
1824 | '々'
1825 | '〆'
1826 | 'ぁ'
1827 | 'ぃ'
1828 | 'ぅ'
1829 | 'ぇ'
1830 | 'ぉ'
1831 | 'っ'
1832 | 'ゃ'
1833 | 'ゅ'
1834 | 'ょ'
1835 | 'ゎ'
1836 | 'ァ'
1837 | 'ィ'
1838 | 'ゥ'
1839 | 'ェ'
1840 | 'ォ'
1841 | 'ッ'
1842 | 'ャ'
1843 | 'ュ'
1844 | 'ョ'
1845 | 'ヮ'
1846 | 'ヶ'
1847 | 'ヵ'
1848 )
1849}
1850
1851pub fn is_line_end_forbidden(c: char) -> bool {
1852 matches!(c, '(' | '[' | '{' | '〈' | '《' | '『' | '「' | '〔')
1853}