use super::super::*;
use super::common::*;
#[test]
fn test_run_is_signed_number() {
assert!(PdfDocument::run_is_signed_number("-1")); assert!(PdfDocument::run_is_signed_number("\u{2212}2")); assert!(PdfDocument::run_is_signed_number("\u{2010}3")); assert!(PdfDocument::run_is_signed_number("\u{2011}45"));
assert!(!PdfDocument::run_is_signed_number("-"));
assert!(!PdfDocument::run_is_signed_number("2"));
assert!(!PdfDocument::run_is_signed_number("th"));
assert!(!PdfDocument::run_is_signed_number(""));
assert!(!PdfDocument::run_is_signed_number("1-"));
}
#[test]
fn test_rotate_span_bbox_identity_and_180() {
let r = crate::geometry::Rect::new(10.0, 20.0, 30.0, 5.0);
let (w, h) = (200.0, 100.0);
let id = PdfDocument::rotate_span_bbox(r, 0, w, h);
assert!((id.x - r.x).abs() < 1e-4 && (id.y - r.y).abs() < 1e-4);
assert!((id.width - r.width).abs() < 1e-4 && (id.height - r.height).abs() < 1e-4);
let m = PdfDocument::rotate_span_bbox(r, 180, w, h);
assert!((m.x - (w - (r.x + r.width))).abs() < 1e-4, "180 x: {}", m.x);
assert!((m.y - (h - (r.y + r.height))).abs() < 1e-4, "180 y: {}", m.y);
assert!((m.width - r.width).abs() < 1e-4 && (m.height - r.height).abs() < 1e-4);
}
#[test]
fn test_rotate_span_bbox_90_270_roundtrip_and_swap() {
let r = crate::geometry::Rect::new(10.0, 20.0, 30.0, 5.0);
let r90 = PdfDocument::rotate_span_bbox(r, 90, 200.0, 100.0);
assert!((r90.width - r.height).abs() < 1e-4, "w/h swap: {}", r90.width);
assert!((r90.height - r.width).abs() < 1e-4, "w/h swap: {}", r90.height);
let s = crate::geometry::Rect::new(12.0, 34.0, 6.0, 8.0);
let p = 100.0;
let a = PdfDocument::rotate_span_bbox(s, 90, p, p);
let b = PdfDocument::rotate_span_bbox(a, 90, p, p);
let c = PdfDocument::rotate_span_bbox(b, 90, p, p);
let d = PdfDocument::rotate_span_bbox(c, 90, p, p);
assert!((d.x - s.x).abs() < 1e-3, "roundtrip x: {} vs {}", d.x, s.x);
assert!((d.y - s.y).abs() < 1e-3, "roundtrip y: {} vs {}", d.y, s.y);
assert!((d.width - s.width).abs() < 1e-3 && (d.height - s.height).abs() < 1e-3);
}
#[test]
fn test_y_band_candidates_is_superset_of_tolerance() {
let band = 4.0_f32;
let spans: Vec<TextSpan> = [0.0, 1.5, 3.9, 4.0, 4.1, 8.0, 100.0, -3.0, -8.0]
.iter()
.map(|&y| make_test_span("x", 0.0, y, 5.0, 10.0))
.collect();
let idx = PdfDocument::build_y_band_index(&spans, band);
for &cy in &[0.0_f32, 4.0, 4.05, 100.0, -3.0] {
let got: std::collections::HashSet<usize> = PdfDocument::y_band_candidates(&idx, cy, band).collect();
for (j, s) in spans.iter().enumerate() {
if (s.bbox.y - cy).abs() <= band {
assert!(
got.contains(&j),
"index missed span {j} (y={}) within band of cy={cy}",
s.bbox.y
);
}
}
}
}
#[test]
fn test_merge_drop_cap_initial() {
let mut spans = vec![
make_test_span("T", 0.0, 100.0, 14.0, 20.0),
make_test_span("ABLE 102.3", 15.0, 100.0, 60.0, 12.0),
];
PdfDocument::merge_drop_cap_initials(&mut spans);
assert_eq!(spans.len(), 1);
assert_eq!(spans[0].text, "TABLE 102.3");
assert_eq!(spans[0].bbox.x, 0.0);
}
#[test]
fn test_merge_drop_cap_skips_same_size_capital() {
let mut spans = vec![
make_test_span("A", 0.0, 100.0, 8.0, 12.0),
make_test_span("word", 9.0, 100.0, 30.0, 12.0),
];
PdfDocument::merge_drop_cap_initials(&mut spans);
assert_eq!(spans.len(), 2, "same-size capital is not a drop-cap initial");
}
#[test]
fn test_merge_drop_cap_skips_math_subscript_base() {
let mut spans = vec![
make_test_span("the shuffle algebra", 0.0, 100.0, 90.0, 10.0),
make_test_span("A", 92.0, 100.0, 7.0, 10.0),
make_test_span("st", 99.0, 98.0, 6.0, 6.5),
make_test_span("of a statistic", 106.0, 100.0, 70.0, 10.0),
];
PdfDocument::merge_drop_cap_initials(&mut spans);
assert_eq!(spans.len(), 4, "inline math base letter is not a drop-cap initial");
assert_eq!(spans[1].text, "A");
assert_eq!(spans[2].text, "st");
}
#[test]
fn test_merge_drop_cap_skips_word_spaced_standalone_capital() {
let mut spans = vec![
make_test_span("ordinary body sentence one", 0.0, 200.0, 120.0, 10.0),
make_test_span("ordinary body sentence two", 0.0, 188.0, 120.0, 10.0),
make_test_span("A", 0.0, 100.0, 12.0, 18.0),
make_test_span("Perspective", 17.0, 100.0, 90.0, 18.0),
];
PdfDocument::merge_drop_cap_initials(&mut spans);
assert_eq!(spans.len(), 4, "word-spaced standalone capital is not a drop cap");
assert_eq!(spans[2].text, "A");
assert_eq!(spans[3].text, "Perspective");
}
#[test]
fn test_order_rotated_blocks_groups_by_rotation() {
let mk = |t: &str, x: f32, y: f32, rot: f32| {
let mut s = make_test_span(t, x, y, 10.0, 10.0);
s.rotation_degrees = rot;
s
};
let spans = vec![
mk("A", 10.0, 50.0, 90.0),
mk("B", 10.0, 80.0, 90.0),
mk("C", 200.0, 50.0, -90.0),
];
let out = PdfDocument::order_rotated_blocks(spans);
assert_eq!(out.len(), 3, "no spans dropped");
let rots: Vec<f32> = out.iter().map(|s| s.rotation_degrees).collect();
assert_eq!(rots, vec![90.0, 90.0, -90.0]);
assert_eq!(out[0].text, "A");
assert_eq!(out[1].text, "B");
}
#[test]
fn test_merge_drop_cap_does_not_reach_line_above() {
let mut spans = vec![
make_test_span("Or", 44.0, 344.0, 14.4, 12.0),
make_test_span("if", 62.0, 342.5, 10.7, 8.9),
make_test_span("Idrop upon my toe", 74.0, 343.9, 90.0, 12.2),
make_test_span("A", 54.7, 328.1, 10.1, 16.8),
make_test_span("very heavy weight", 69.8, 327.8, 90.0, 8.4),
];
PdfDocument::merge_drop_cap_initials(&mut spans);
assert!(
spans.iter().all(|s| s.text != "Aif" && !s.text.contains("OrA")),
"tall initial must not steal a word from the line above"
);
assert!(
spans.iter().any(|s| s.text == "A"),
"initial left intact on its own line"
);
}
#[test]
fn test_fix_digit_logicalnot_decimal() {
assert_eq!(PdfDocument::fix_digit_logicalnot_decimal("1\u{00AC}00"), "1.00");
assert_eq!(
PdfDocument::fix_digit_logicalnot_decimal("0\u{00AC}75 1\u{00AC}00"),
"0.75 1.00"
);
assert_eq!(
PdfDocument::fix_digit_logicalnot_decimal("A \u{00AC} B"),
"A \u{00AC} B"
);
assert_eq!(
PdfDocument::fix_digit_logicalnot_decimal("5 \u{00AC} 3"),
"5 \u{00AC} 3"
);
assert_eq!(PdfDocument::fix_digit_logicalnot_decimal("\u{00AC}5"), "\u{00AC}5");
assert_eq!(PdfDocument::fix_digit_logicalnot_decimal("1\u{00AC} 00"), "1.00");
assert_eq!(
PdfDocument::fix_digit_logicalnot_decimal("0\u{00AC} 75 1\u{00AC} 00"),
"0.75 1.00"
);
assert_eq!(
PdfDocument::fix_digit_logicalnot_decimal("5 \u{00AC} 3"),
"5 \u{00AC} 3"
);
assert_eq!(
PdfDocument::fix_digit_logicalnot_decimal("1\u{00AC} 00"),
"1\u{00AC} 00"
);
}