use crate::parser::{ParseError, ParseResult};
use crate::text::fonts::cff::types::{
encode_cff_int_5byte, read_u16, CffDictScanner, CffDictToken,
};
#[derive(Debug, Default)]
pub(crate) struct TopDictOffsets {
pub(crate) charstrings_offset: Option<i32>,
pub(crate) charset_offset: Option<i32>,
pub(crate) private_dict: Option<(i32, i32)>,
pub(crate) fd_array_offset: Option<i32>,
pub(crate) fd_select_offset: Option<i32>,
}
pub(crate) fn parse_top_dict(data: &[u8]) -> TopDictOffsets {
let mut offsets = TopDictOffsets::default();
let mut operand_stack: Vec<i32> = Vec::new();
for token in CffDictScanner::new(data) {
match token {
CffDictToken::Operand(v) => {
operand_stack.push(v);
}
CffDictToken::EscapedOperator(op2) => {
match op2 {
36 => {
if let Some(&v) = operand_stack.last() {
offsets.fd_array_offset = Some(v);
}
}
37 => {
if let Some(&v) = operand_stack.last() {
offsets.fd_select_offset = Some(v);
}
}
_ => {}
}
operand_stack.clear();
}
CffDictToken::Operator(b) => {
match b {
15 => {
if let Some(&v) = operand_stack.last() {
offsets.charset_offset = Some(v);
}
}
17 => {
if let Some(&v) = operand_stack.last() {
offsets.charstrings_offset = Some(v);
}
}
18 => {
if operand_stack.len() >= 2 {
let offset = operand_stack[operand_stack.len() - 1];
let size = operand_stack[operand_stack.len() - 2];
offsets.private_dict = Some((size, offset));
}
}
_ => {}
}
operand_stack.clear();
}
}
}
offsets
}
pub(crate) fn build_cid_top_dict(
original_sid_top_dict: &[u8],
num_glyphs: i32,
charset_offset: i32,
charstrings_offset: i32,
fd_array_offset: i32,
fd_select_offset: i32,
) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&encode_cff_int_5byte(138));
out.extend_from_slice(&encode_cff_int_5byte(139));
out.extend_from_slice(&encode_cff_int_5byte(0));
out.push(12);
out.push(30);
if let Some(font_bbox_bytes) = extract_operator_group(original_sid_top_dict, 5, false) {
out.extend_from_slice(&font_bbox_bytes);
}
out.extend_from_slice(&encode_cff_int_5byte(num_glyphs));
out.push(12);
out.push(34);
out.extend_from_slice(&encode_cff_int_5byte(charset_offset));
out.push(15);
out.extend_from_slice(&encode_cff_int_5byte(charstrings_offset));
out.push(17);
out.extend_from_slice(&encode_cff_int_5byte(fd_array_offset));
out.push(12);
out.push(36);
out.extend_from_slice(&encode_cff_int_5byte(fd_select_offset));
out.push(12);
out.push(37);
out
}
pub(crate) fn build_minimal_fd_dict(private_size: i32, private_offset: i32) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&encode_cff_int_5byte(private_size));
out.extend_from_slice(&encode_cff_int_5byte(private_offset));
out.push(18);
out
}
fn extract_operator_group(dict: &[u8], target_op: u8, escaped: bool) -> Option<Vec<u8>> {
let mut scanner = CffDictScanner::new(dict);
let mut operand_start = 0usize;
while let Some(token) = scanner.next() {
match token {
CffDictToken::Operand(_) => {}
CffDictToken::EscapedOperator(op) => {
if escaped && op == target_op {
return Some(dict[operand_start..scanner.position()].to_vec());
}
operand_start = scanner.position();
}
CffDictToken::Operator(b) => {
if !escaped && b == target_op {
return Some(dict[operand_start..scanner.position()].to_vec());
}
operand_start = scanner.position();
}
}
}
None
}
pub(crate) fn rebuild_fd_dict(original: &[u8], private_size: i32, private_offset: i32) -> Vec<u8> {
let mut out = Vec::new();
let mut scanner = CffDictScanner::new(original);
let mut operand_start = 0usize;
loop {
let token = match scanner.next() {
Some(t) => t,
None => break,
};
match token {
CffDictToken::Operand(_) => {
}
CffDictToken::EscapedOperator(_) => {
out.extend_from_slice(&original[operand_start..scanner.position()]);
operand_start = scanner.position();
}
CffDictToken::Operator(b) => {
match b {
18 => {
out.extend_from_slice(&encode_cff_int_5byte(private_size));
out.extend_from_slice(&encode_cff_int_5byte(private_offset));
out.push(18);
}
_ => {
out.extend_from_slice(&original[operand_start..scanner.position()]);
}
}
operand_start = scanner.position();
}
}
}
out
}
pub(crate) fn parse_fd_select(
cff: &[u8],
offset: usize,
num_glyphs: usize,
) -> ParseResult<Vec<u8>> {
if offset >= cff.len() {
return Err(ParseError::SyntaxError {
position: offset,
message: "FDSelect offset out of range".to_string(),
});
}
let format = cff[offset];
match format {
0 => {
if offset + 1 + num_glyphs > cff.len() {
return Err(ParseError::SyntaxError {
position: offset,
message: "FDSelect Format 0 truncated".to_string(),
});
}
Ok(cff[offset + 1..offset + 1 + num_glyphs].to_vec())
}
3 => {
if offset + 3 > cff.len() {
return Err(ParseError::SyntaxError {
position: offset,
message: "FDSelect Format 3 header truncated".to_string(),
});
}
let n_ranges = read_u16(cff, offset + 1)? as usize;
let ranges_end = offset + 3 + n_ranges * 3 + 2;
if ranges_end > cff.len() {
return Err(ParseError::SyntaxError {
position: offset,
message: "FDSelect Format 3 ranges truncated".to_string(),
});
}
let mut result = vec![0u8; num_glyphs];
for i in 0..n_ranges {
let range_base = offset + 3 + i * 3;
let first_gid = read_u16(cff, range_base)? as usize;
let fd_idx = cff[range_base + 2];
let end_gid = if i + 1 < n_ranges {
read_u16(cff, offset + 3 + (i + 1) * 3)? as usize
} else {
read_u16(cff, offset + 3 + n_ranges * 3)? as usize
};
let end_gid = end_gid.min(num_glyphs);
for gid in first_gid..end_gid {
if gid < result.len() {
result[gid] = fd_idx;
}
}
}
Ok(result)
}
_ => Err(ParseError::SyntaxError {
position: offset,
message: format!("FDSelect format {} not supported", format),
}),
}
}
pub(crate) fn parse_fd_private(fd_dict: &[u8]) -> Option<(i32, i32)> {
let mut operand_stack: Vec<i32> = Vec::new();
for token in CffDictScanner::new(fd_dict) {
match token {
CffDictToken::Operand(v) => {
operand_stack.push(v);
}
CffDictToken::EscapedOperator(_) => {
operand_stack.clear();
}
CffDictToken::Operator(b) => {
if b == 18 && operand_stack.len() >= 2 {
let offset = operand_stack[operand_stack.len() - 1];
let size = operand_stack[operand_stack.len() - 2];
return Some((size, offset));
}
operand_stack.clear();
}
}
}
None
}
pub(crate) struct FdData {
pub(crate) fd_dict_bytes: Vec<u8>,
pub(crate) private_bytes: Vec<u8>,
pub(crate) local_subr_bytes: Vec<u8>,
}
pub(crate) fn parse_local_subrs_offset(private_dict: &[u8]) -> Option<usize> {
let mut operand_stack: Vec<i32> = Vec::new();
for token in CffDictScanner::new(private_dict) {
match token {
CffDictToken::Operand(v) => {
operand_stack.push(v);
}
CffDictToken::EscapedOperator(_) => {
operand_stack.clear();
}
CffDictToken::Operator(b) => {
if b == 19 {
return operand_stack.last().copied().and_then(|v| {
if v > 0 {
Some(v as usize)
} else {
None
}
});
}
operand_stack.clear();
}
}
}
None
}
pub(crate) fn strip_private_subrs_op(private_dict: &mut Vec<u8>) {
let mut scanner = CffDictScanner::new(private_dict);
let mut op19_operand_start: Option<usize> = None;
let mut op19_end: Option<usize> = None;
let mut operand_start = 0usize;
loop {
let token = match scanner.next() {
Some(t) => t,
None => break,
};
match token {
CffDictToken::Operand(_) => {}
CffDictToken::EscapedOperator(_) => {
operand_start = scanner.position();
}
CffDictToken::Operator(b) => {
if b == 19 {
op19_operand_start = Some(operand_start);
op19_end = Some(scanner.position());
}
operand_start = scanner.position();
}
}
}
if let (Some(start), Some(end)) = (op19_operand_start, op19_end) {
private_dict.drain(start..end);
}
}