use super::error::{DocError, Result};
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Fib {
pub version: u16,
pub use_table1: bool,
pub clx_offset: u32,
pub clx_size: u32,
pub text_len: u32,
pub footnote_len: u32,
pub header_len: u32,
pub comment_len: u32,
pub endnote_len: u32,
pub textbox_len: u32,
pub header_textbox_len: u32,
}
impl Fib {
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 68 {
return Err(DocError::InvalidFib(format!(
"WordDocument stream too short: {} bytes",
data.len()
)));
}
let wident = u16::from_le_bytes([data[0], data[1]]);
if wident != 0xA5EC && wident != 0xA5DC {
return Err(DocError::InvalidFib(format!("unknown wIdent: 0x{wident:04X}")));
}
let version = u16::from_le_bytes([data[2], data[3]]);
let flags = u16::from_le_bytes([data[0x0A], data[0x0B]]);
let use_table1 = (flags & (1 << 9)) != 0;
let text_len = read_u32(data, 0x4C);
let footnote_len = read_u32(data, 0x50);
let header_len = read_u32(data, 0x54);
let comment_len = read_u32(data, 0x58);
let endnote_len = read_u32(data, 0x5C);
let textbox_len = read_u32(data, 0x60);
let header_textbox_len = read_u32(data, 0x64);
let (clx_offset, clx_size) = if data.len() > 0x01AA {
(read_u32(data, 0x01A2), read_u32(data, 0x01A6))
} else {
(0, 0)
};
Ok(Self {
version,
use_table1,
clx_offset,
clx_size,
text_len,
footnote_len,
header_len,
comment_len,
endnote_len,
textbox_len,
header_textbox_len,
})
}
}
fn read_u32(data: &[u8], offset: usize) -> u32 {
if offset + 4 <= data.len() {
u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
])
} else {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
fn build_minimal_fib() -> Vec<u8> {
let mut data = vec![0u8; 1024];
data[0..2].copy_from_slice(&0xA5ECu16.to_le_bytes());
data[2..4].copy_from_slice(&0x00C1u16.to_le_bytes());
data[0x0A..0x0C].copy_from_slice(&(1u16 << 9).to_le_bytes());
data[0x4C..0x50].copy_from_slice(&100u32.to_le_bytes());
data[0x01A2..0x01A6].copy_from_slice(&512u32.to_le_bytes());
data[0x01A6..0x01AA].copy_from_slice(&64u32.to_le_bytes());
data
}
#[test]
fn parse_valid_fib() {
let data = build_minimal_fib();
let fib = Fib::parse(&data).unwrap();
assert_eq!(fib.version, 0x00C1);
assert!(fib.use_table1);
assert_eq!(fib.text_len, 100);
assert_eq!(fib.clx_offset, 512);
assert_eq!(fib.clx_size, 64);
}
#[test]
fn bad_wident_rejected() {
let mut data = build_minimal_fib();
data[0..2].copy_from_slice(&0x1234u16.to_le_bytes());
assert!(Fib::parse(&data).is_err());
}
#[test]
fn too_short_rejected() {
let data = vec![0u8; 100];
assert!(Fib::parse(&data).is_err());
}
#[test]
fn use_table0() {
let mut data = build_minimal_fib();
data[0x0A..0x0C].copy_from_slice(&0u16.to_le_bytes()); let fib = Fib::parse(&data).unwrap();
assert!(!fib.use_table1);
}
}