89 lines
4.0 KiB
Rust
89 lines
4.0 KiB
Rust
//! Ely's edits read back by another reader, PDFKit (QA-13).
|
|
#![cfg(target_os = "macos")]
|
|
|
|
use std::path::Path;
|
|
|
|
use ely_pdf_engine::{FieldKind, NewField, Worker, Writing, Wrote, sample::{Extras, pdf_with}};
|
|
use objc2::AllocAnyThread as _;
|
|
use objc2_foundation::{NSString, NSURL};
|
|
use objc2_pdf_kit::PDFDocument;
|
|
|
|
fn worker() -> Worker {
|
|
Worker::spawn_from(Path::new(env!("CARGO_BIN_EXE_ely-pdf-worker"))).expect("the worker")
|
|
}
|
|
|
|
/// Each page's text and widget values, as PDFKit reads them.
|
|
fn read_back(path: &Path) -> Vec<(String, Vec<String>)> {
|
|
let url = NSURL::fileURLWithPath(&NSString::from_str(path.to_str().expect("UTF-8")));
|
|
let document = unsafe { PDFDocument::initWithURL(PDFDocument::alloc(), &url) }.expect("PDFKit opens it");
|
|
(0..unsafe { document.pageCount() })
|
|
.map(|at| {
|
|
let page = unsafe { document.pageAtIndex(at) }.expect("a page");
|
|
let text = unsafe { page.string() }.map(|s| s.to_string()).unwrap_or_default();
|
|
let values = unsafe { page.annotations() }.to_vec().into_iter().filter_map(|a| unsafe { a.widgetStringValue() }.map(|s| s.to_string())).collect();
|
|
(text, values)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[test]
|
|
fn text_a_field_and_page_order_read_back_elsewhere() {
|
|
let dir = tempfile::tempdir().expect("a temp dir");
|
|
let path = dir.path().join("doc.pdf");
|
|
std::fs::write(&path, pdf_with(&[(612.0, 792.0, &["First page"]), (612.0, 792.0, &["Second page"])], &[], &Extras::default())).expect("writes");
|
|
let mut worker = worker();
|
|
let mut doc = worker.open(&path).expect("opens").doc;
|
|
let block = worker.text_blocks(doc, 0).expect("blocks").into_iter().position(|b| b.text.contains("First page")).expect("the block");
|
|
let writing = Writing {
|
|
block: Some(block),
|
|
expected: "First page".into(),
|
|
text: "Changed words".into(),
|
|
font: "helvetica".into(),
|
|
size: 12.0,
|
|
rgb: 0,
|
|
bx: [72.0, 60.0, 400.0],
|
|
};
|
|
assert!(matches!(worker.put_text(doc, 0, &writing, 0.0).expect("writes"), Wrote::Done(_)));
|
|
doc = worker.now(doc).expect("the state");
|
|
let field = NewField { kind: FieldKind::Text, name: "name".into(), rect: [72.0, 200.0, 272.0, 220.0], options: Vec::new() };
|
|
worker.add_field(doc, 0, &field).expect("adds a field");
|
|
doc = worker.now(doc).expect("the state");
|
|
let id = worker.marks(doc, 0).expect("marks").fields[0].id;
|
|
worker.fill(doc, 0, id, "Ada").expect("fills");
|
|
doc = worker.now(doc).expect("the state");
|
|
worker.move_pages(doc, &[1], 0).expect("moves");
|
|
doc = worker.now(doc).expect("the state");
|
|
let out = dir.path().join("edited.pdf");
|
|
worker.save(doc, &out).expect("saves");
|
|
let pages = read_back(&out);
|
|
assert_eq!(pages.len(), 2);
|
|
assert!(pages[0].0.contains("Second page"), "{pages:?}");
|
|
assert!(pages[1].0.contains("Changed words") && !pages[1].0.contains("First page"), "{pages:?}");
|
|
assert_eq!(pages[1].1, vec!["Ada".to_string()]);
|
|
}
|
|
|
|
#[test]
|
|
fn text_in_a_subset_font_reads_back_elsewhere() {
|
|
let dir = tempfile::tempdir().expect("a temp dir");
|
|
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/fixtures/office/t.pdf");
|
|
let mut worker = worker();
|
|
let mut doc = worker.open(&path).expect("opens").doc;
|
|
let blocks = worker.text_blocks(doc, 0).expect("blocks");
|
|
let (block, read) = blocks.iter().enumerate().find(|(_, b)| b.text.contains("BUNDLE")).expect("the block");
|
|
let writing = Writing {
|
|
block: Some(block),
|
|
expected: read.text.clone(),
|
|
text: "TEXT BUNDLE".into(),
|
|
font: "original".into(),
|
|
size: read.size,
|
|
rgb: read.rgb,
|
|
bx: [read.rect[0], read.rect[1], (read.rect[2] - read.rect[0]) * 2.0],
|
|
};
|
|
assert!(matches!(worker.put_text(doc, 0, &writing, 0.0).expect("writes"), Wrote::Done(_)));
|
|
doc = worker.now(doc).expect("the state");
|
|
let out = dir.path().join("edited.pdf");
|
|
worker.save(doc, &out).expect("saves");
|
|
let pages = read_back(&out);
|
|
assert!(pages[0].0.contains("TEXT BUNDLE"), "{pages:?}");
|
|
}
|