Skip to main content

docvortex_core/
extraction.rs

1//! 仅处理 PDFium 已读出的数值;不持有、传递或调用任何原生 PDF 句柄。
2
3use crate::geometry::{Box4, Size};
4pub type RawGeometry = (Box4, Option<Box4>, Option<Box4>, Option<Size>);
5pub type VisualGeometry = (Box4, Option<Box4>, Option<Box4>, Option<Size>);
6
7/// 与 Python min 保持相同的有符号零及 NaN 首项语义。
8fn minimum(a: f64, b: f64) -> f64 {
9    if b < a {
10        b
11    } else {
12        a
13    }
14}
15/// 与 Python max 保持相同的首项语义。
16fn maximum(a: f64, b: f64) -> f64 {
17    if b > a {
18        b
19    } else {
20        a
21    }
22}
23
24/// 把 PDF 原始点转为视觉页面坐标,保留浮点页面边界。
25fn point(p: Size, frame: Box4, angle: i32) -> Size {
26    let w = (frame[2] - frame[0]).abs();
27    let h = (frame[3] - frame[1]).abs();
28    let x = p[0] - minimum(frame[0], frame[2]);
29    let y = maximum(frame[1], frame[3]) - p[1];
30    match angle.rem_euclid(360) {
31        90 => [h - y, x],
32        180 => [w - x, h - y],
33        270 => [y, w - x],
34        _ => [x, y],
35    }
36}
37
38/// 按原四角遍历次序转换扩展框,非有限或零面积返回空值。
39fn visual(raw: Option<Box4>, frame: Box4, angle: i32) -> Option<Box4> {
40    let b = raw?;
41    let points =
42        [[b[0], b[1]], [b[0], b[3]], [b[2], b[1]], [b[2], b[3]]].map(|p| point(p, frame, angle));
43    let mut out = [points[0][0], points[0][1], points[0][0], points[0][1]];
44    for p in &points[1..] {
45        out = [
46            minimum(out[0], p[0]),
47            minimum(out[1], p[1]),
48            maximum(out[2], p[0]),
49            maximum(out[3], p[1]),
50        ];
51    }
52    if out.iter().all(|v| v.is_finite()) && out[2] > out[0] && out[3] > out[1] {
53        Some(out)
54    } else {
55        None
56    }
57}
58
59/// 批量物化布局框与扩展框,布局框仍使用原有整数页面高度。
60pub fn materialize(
61    rows: Vec<RawGeometry>,
62    frame: Box4,
63    rounded: Size,
64    angle: i32,
65) -> Vec<VisualGeometry> {
66    rows.into_iter()
67        .map(|(selected, loose, tight, origin)| {
68            let y0 = rounded[1] - (selected[1] - frame[1]);
69            let y1 = rounded[1] - (selected[3] - frame[1]);
70            let mut layout = [
71                minimum(selected[0], selected[2]) - frame[0],
72                minimum(y0, y1),
73                maximum(selected[0], selected[2]) - frame[0],
74                maximum(y0, y1),
75            ];
76            if angle != 0 {
77                layout = crate::geometry::rotate(layout, rounded, (360 - angle).rem_euclid(360));
78                layout = [
79                    minimum(layout[0], layout[2]),
80                    minimum(layout[1], layout[3]),
81                    maximum(layout[0], layout[2]),
82                    maximum(layout[1], layout[3]),
83                ];
84            }
85            let origin = origin
86                .map(|p| point(p, frame, angle))
87                .filter(|p| p.iter().all(|v| v.is_finite()));
88            (
89                layout,
90                visual(loose, frame, angle),
91                visual(tight, frame, angle),
92                origin,
93            )
94        })
95        .collect()
96}