Dot 2: CFF/Type1C tran boc thanh OTF (cffwrap parser toi thieu: CharStrings/Encoding/charset/FontMatrix; hmtx tu /Widths, asc/desc tu FontDescriptor); tounicode thu widths/DW/ascent/descent; goi CJK tuy chon (fetch-fonts-cjk.ps1 + ff-app-cjk lazy theo noi dung)
Build Windows portable / build (push) Canceled after 0s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 16:55:06 +07:00
co-authored by Claude Fable 5
parent fd90a124dc
commit 57b8b84f7d
4 changed files with 551 additions and 2 deletions
+88 -1
View File
@@ -92,6 +92,22 @@ fn scan_resources(
.unwrap_or_default();
let is_cid = subtype == b"Type0";
let mut cid_to_gid = None;
let mut widths: HashMap<u32, f32> = HashMap::new();
let mut default_width = 1000.0f32;
let mut ascent = 0.0f32;
let mut descent = 0.0f32;
let mut read_descriptor = |d: &Dictionary, asc: &mut f32, desc: &mut f32| {
if let Ok(x) = d.get(b"FontDescriptor") {
if let Ok(x) = deref(doc, x).as_dict() {
if let Some(v) = x.get(b"Ascent").ok().and_then(|o| as_num(doc, o)) {
*asc = v;
}
if let Some(v) = x.get(b"Descent").ok().and_then(|o| as_num(doc, o)) {
*desc = v;
}
}
}
};
if is_cid {
if let Ok(desc) = fd.get(b"DescendantFonts") {
let desc = deref(doc, desc);
@@ -112,13 +128,46 @@ fn scan_resources(
);
}
}
// /W: [c [w..] | c1 c2 w] ; /DW: bề rộng mặc định.
if let Ok(w) = dd.get(b"W") {
if let Object::Array(arr) = deref(doc, w) {
parse_cid_widths(doc, arr, &mut widths);
}
}
if let Some(v) = dd.get(b"DW").ok().and_then(|o| as_num(doc, o)) {
default_width = v;
}
read_descriptor(dd, &mut ascent, &mut descent);
}
}
} else {
// Font đơn giản: /FirstChar + /Widths.
if let (Some(fc), Ok(ws)) = (
fd.get(b"FirstChar").ok().and_then(|o| as_num(doc, o)),
fd.get(b"Widths"),
) {
if let Object::Array(arr) = deref(doc, ws) {
for (i, w) in arr.iter().enumerate() {
if let Some(w) = as_num(doc, w) {
widths.insert(fc as u32 + i as u32, w);
}
}
}
}
read_descriptor(fd, &mut ascent, &mut descent);
}
if code_to_uni.is_empty() {
continue; // không có ToUnicode — thử font trùng tên khác
}
return Some(FontMapping { code_to_uni, is_cid, cid_to_gid });
return Some(FontMapping {
code_to_uni,
is_cid,
cid_to_gid,
widths,
default_width,
ascent,
descent,
});
}
}
}
@@ -141,6 +190,44 @@ fn scan_resources(
None
}
/// Số PDF (Integer/Real, kể cả qua reference) → f32.
fn as_num(doc: &LoDoc, o: &Object) -> Option<f32> {
match deref(doc, o) {
Object::Integer(i) => Some(*i as f32),
Object::Real(r) => Some(*r),
_ => None,
}
}
/// Mảng /W của font CID: dạng `c [w1 w2 …]` (mã c..c+n-1) hoặc `c1 c2 w`.
fn parse_cid_widths(doc: &LoDoc, arr: &[Object], out: &mut HashMap<u32, f32>) {
let mut i = 0;
while i < arr.len() {
let Some(c1) = as_num(doc, &arr[i]) else { break };
match arr.get(i + 1).map(|o| deref(doc, o)) {
Some(Object::Array(list)) => {
for (k, w) in list.iter().enumerate() {
if let Some(w) = as_num(doc, w) {
out.insert(c1 as u32 + k as u32, w);
}
}
i += 2;
}
Some(_) => {
let c2 = arr.get(i + 1).and_then(|o| as_num(doc, o));
let w = arr.get(i + 2).and_then(|o| as_num(doc, o));
let (Some(c2), Some(w)) = (c2, w) else { break };
let hi = (c2 as u32).min(c1 as u32 + 0xFFFF);
for c in (c1 as u32)..=hi {
out.insert(c, w);
}
i += 3;
}
None => break,
}
}
}
enum Tok {
Hex(Vec<u8>),
Kw(String),