use super::sfnt::sfnt_search_params;
use crate::font::FontError;
use std::collections::BTreeMap;
pub(super) fn build_cmap_format4(char_to_new_gid: &BTreeMap<char, u16>) -> Result<Vec<u8>, FontError> {
let mut pairs: Vec<(u32, u16)> = char_to_new_gid
.iter()
.filter(|(&ch, _)| u32::from(ch) <= 0xFFFF)
.map(|(&ch, &gid)| (u32::from(ch), gid))
.collect();
pairs.sort_unstable_by_key(|&(cp, _)| cp);
struct Run {
start: u32,
end: u32,
gids: Vec<u16>,
}
let mut runs: Vec<Run> = Vec::new();
for (cp, gid) in pairs {
if let Some(last) = runs.last_mut() {
if cp == last.end + 1 {
last.end = cp;
last.gids.push(gid);
continue;
}
}
runs.push(Run {
start: cp,
end: cp,
gids: vec![gid],
});
}
let seg_count = runs.len() + 1; let seg_count_x2 = u16::try_from(seg_count * 2).map_err(|_| FontError::MalformedFont)?;
let seg_count_u16 = seg_count_x2 / 2;
let (search_range, entry_selector, range_shift) = sfnt_search_params(seg_count_u16, 2);
let end_code_start = 14usize;
let start_code_start = end_code_start + seg_count * 2 + 2; let id_delta_start = start_code_start + seg_count * 2;
let id_range_offset_start = id_delta_start + seg_count * 2;
let glyph_id_array_start = id_range_offset_start + seg_count * 2;
let mut glyph_id_array: Vec<u16> = Vec::new();
let mut run_glyph_offsets = Vec::with_capacity(runs.len());
for run in &runs {
run_glyph_offsets.push(glyph_id_array.len());
glyph_id_array.extend_from_slice(&run.gids);
}
let length = glyph_id_array_start + glyph_id_array.len() * 2;
let length_u16 = u16::try_from(length).map_err(|_| FontError::MalformedFont)?;
let mut sub = vec![0u8; length];
sub[0..2].copy_from_slice(&4u16.to_be_bytes());
sub[2..4].copy_from_slice(&length_u16.to_be_bytes());
sub[4..6].copy_from_slice(&0u16.to_be_bytes()); sub[6..8].copy_from_slice(&seg_count_x2.to_be_bytes());
sub[8..10].copy_from_slice(&search_range.to_be_bytes());
sub[10..12].copy_from_slice(&entry_selector.to_be_bytes());
sub[12..14].copy_from_slice(&range_shift.to_be_bytes());
for (i, run) in runs.iter().enumerate() {
let run_end = u16::try_from(run.end).map_err(|_| FontError::MalformedFont)?;
let run_start = u16::try_from(run.start).map_err(|_| FontError::MalformedFont)?;
sub[end_code_start + i * 2..end_code_start + i * 2 + 2].copy_from_slice(&run_end.to_be_bytes());
sub[start_code_start + i * 2..start_code_start + i * 2 + 2].copy_from_slice(&run_start.to_be_bytes());
sub[id_delta_start + i * 2..id_delta_start + i * 2 + 2].copy_from_slice(&0i16.to_be_bytes());
let id_range_offset_pos = id_range_offset_start + i * 2;
let glyph_id_array_byte_offset = glyph_id_array_start + run_glyph_offsets[i] * 2;
let id_range_offset_value =
u16::try_from(glyph_id_array_byte_offset - id_range_offset_pos).map_err(|_| FontError::MalformedFont)?;
sub[id_range_offset_pos..id_range_offset_pos + 2].copy_from_slice(&id_range_offset_value.to_be_bytes());
}
let term = seg_count - 1;
sub[end_code_start + term * 2..end_code_start + term * 2 + 2].copy_from_slice(&0xFFFFu16.to_be_bytes());
sub[start_code_start + term * 2..start_code_start + term * 2 + 2].copy_from_slice(&0xFFFFu16.to_be_bytes());
sub[id_delta_start + term * 2..id_delta_start + term * 2 + 2].copy_from_slice(&1i16.to_be_bytes());
sub[id_range_offset_start + term * 2..id_range_offset_start + term * 2 + 2].copy_from_slice(&0u16.to_be_bytes());
for (i, gid) in glyph_id_array.iter().enumerate() {
let pos = glyph_id_array_start + i * 2;
sub[pos..pos + 2].copy_from_slice(&gid.to_be_bytes());
}
let mut cmap = Vec::with_capacity(12 + sub.len());
cmap.extend_from_slice(&0u16.to_be_bytes()); cmap.extend_from_slice(&1u16.to_be_bytes()); cmap.extend_from_slice(&3u16.to_be_bytes()); cmap.extend_from_slice(&1u16.to_be_bytes()); cmap.extend_from_slice(&12u32.to_be_bytes()); cmap.extend_from_slice(&sub);
Ok(cmap)
}