makepad/libs/pdf_parse/examples/pdfdump.rs
Admin d25cce9ae6 mp*: the window-manager app family — quick-look, warm-instance pool, pty teardown, theme bridge
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- mpwm: ghost-launch/menu-flash root cause, scroll z-gate, browser binds, path-free status, viewers delisted, Fab opens
- mpwm: Hyprland-exact close animation over a frozen snapshot
- mpwm: warm-instance pool, flat-luminance opens, flicker-free CEF resize
- work: land the sources the last commits reference
- mpwm: quick-look gap fixes; image cache eviction on preview unload
- tweaker: material thumbnails + vibecode popup + ctrl-space notes, undo/redo over the edit ledger, capture-semantics pi
- tweaker: tabbed side panel (Props/Shader/Tree) - shader tab with checkerboard material well + prompt, complete widget-
- mpwm+mpterm: text quick-look — pty teardown deadlock fix, in-place retarget verified, debug probes stripped
2026-08-29 09:26:26 +02:00

83 lines
3.2 KiB
Rust

//! Dump a PDF's fonts and first text ops — the diagnosis instrument for
//! text-positioning bugs. `cargo run -p makepad-pdf-parse --example pdfdump
//! --release -- <file.pdf> [page] [max_ops]`.
use makepad_pdf_parse::content::parse_content_stream;
use makepad_pdf_parse::document::PdfDocument;
use makepad_pdf_parse::PdfOp;
fn main() {
let mut args = std::env::args().skip(1);
let path = args.next().expect("usage: pdfdump <file.pdf> [page] [max]");
let page_no: usize = args.next().and_then(|s| s.parse().ok()).unwrap_or(0);
let max_ops: usize = args.next().and_then(|s| s.parse().ok()).unwrap_or(160);
let data = std::fs::read(&path).expect("read pdf");
let mut doc = PdfDocument::parse(&data).expect("parse pdf");
println!("pages: {}", doc.page_count());
let page = doc.page(page_no).expect("page");
println!(
"page {}: media {:?} crop {:?} rotate {} content {} bytes",
page_no,
page.media_box,
page.crop_box,
page.rotate,
page.content_data.len()
);
for (name, f) in &page.fonts {
println!(
"font {}: subtype={} base={} enc={:?} first={} last={} widths={} dw={} cidw={:?} tounicode={}",
name,
f.subtype,
f.base_font,
f.encoding,
f.first_char,
f.last_char,
f.widths.len(),
f.default_width,
f.cid_widths.as_ref().map(|w| w.len()),
f.to_unicode.as_ref().map(|t| t.mappings.len()).unwrap_or(0)
);
}
let ops = parse_content_stream(&page.content_data).expect("content");
println!("ops: {}", ops.len());
let mut shown = 0usize;
for op in &ops {
let line = match op {
PdfOp::BeginText => "BT".to_string(),
PdfOp::EndText => "ET".to_string(),
PdfOp::SetFont(n, s) => format!("Tf /{} {}", n, s),
PdfOp::SetTextMatrix(m) => format!("Tm {:?}", m),
PdfOp::MoveText(x, y) => format!("Td {} {}", x, y),
PdfOp::MoveTextSetLeading(x, y) => format!("TD {} {}", x, y),
PdfOp::NextLine => "T*".to_string(),
PdfOp::SetTextLeading(v) => format!("TL {}", v),
PdfOp::ConcatMatrix(m) => format!("cm {:?}", m),
PdfOp::SaveState => "q".to_string(),
PdfOp::RestoreState => "Q".to_string(),
PdfOp::ShowText(b) => format!("Tj {:?}", String::from_utf8_lossy(b)),
PdfOp::ShowTextArray(items) => {
let mut s = String::from("TJ [");
for it in items {
match it {
makepad_pdf_parse::TextArrayItem::Text(b) => {
s.push_str(&format!("{:?} ", String::from_utf8_lossy(b)))
}
makepad_pdf_parse::TextArrayItem::Adjustment(a) => {
s.push_str(&format!("{} ", a))
}
}
}
s.push(']');
s
}
_ => continue,
};
println!(" {}", line);
shown += 1;
if shown >= max_ops {
println!(" … (truncated)");
break;
}
}
}