mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-12 00:41:12 -06:00
H: place a chosen PDF page (Image Import Options)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1b778945af
commit
175aaf28fd
@@ -77,7 +77,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Place…",
|
||||
["File"],
|
||||
Some("Cmd+D"),
|
||||
"{path?|base64?, name?, frame?: id (place into), spread?, x?, y?, width?} — places an image (into the selected empty frame if any); text files (.txt, .docx, .rtf, .md) go into the insertion point, the selected frame or a new frame on `page`/`rect` — {autoflow?: adds pages with threaded frames until the text fits, removeStyles?}",
|
||||
"{path?|base64?, name?, frame?: id (place into), spread?, x?, y?, width?, pdfPage?: n (1-based, Image Import Options)} — places an image (into the selected empty frame if any); text files (.txt, .docx, .rtf, .md) go into the insertion point, the selected frame or a new frame on `page`/`rect` — {autoflow?: adds pages with threaded frames until the text fits, removeStyles?}",
|
||||
has_doc,
|
||||
file_place
|
||||
),
|
||||
@@ -203,7 +203,23 @@ fn file_place(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if designcraft_textimport::is_text_file(&name) {
|
||||
return super::place_text::place_text(s, p, &name, &bytes);
|
||||
}
|
||||
let (pw, ph) = designcraft_render::image_size(&bytes).ok_or_else(|| bad("file.place", "unsupported or corrupt image"))?;
|
||||
// Image Import Options: which page of a PDF (1-based).
|
||||
let pdf_page = match p.get("pdfPage").and_then(Value::as_u64) {
|
||||
Some(n) if designcraft_render::is_pdf(&bytes) => {
|
||||
let count = designcraft_render::pdf_page_count(&bytes).unwrap_or(1);
|
||||
if n == 0 || n as usize > count {
|
||||
return Err(bad("file.place", format!("the PDF has {count} page(s)")));
|
||||
}
|
||||
n as u32 - 1
|
||||
}
|
||||
_ => 0,
|
||||
};
|
||||
let (pw, ph) = match pdf_page {
|
||||
0 => designcraft_render::image_size(&bytes).ok_or_else(|| bad("file.place", "unsupported or corrupt image"))?,
|
||||
n => designcraft_render::pdf_page_size(&bytes, n as usize)
|
||||
.map(|(w, h)| (w.round().max(1.0) as u32, h.round().max(1.0) as u32))
|
||||
.ok_or_else(|| bad("file.place", "can't read that PDF page"))?,
|
||||
};
|
||||
// 72 ppi by default unless the file says otherwise; scale so it fits the page when huge.
|
||||
let (nw, nh) = (pw as f64, ph as f64);
|
||||
let target_frame = super::id_param(p, "frame").or_else(|| {
|
||||
@@ -218,7 +234,7 @@ fn file_place(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
s.edit(|d, sel| {
|
||||
let aid = AssetId(d.alloc());
|
||||
let mime = designcraft_render::image_mime(&bytes).to_string();
|
||||
d.assets.insert(aid, Arc::new(Asset { id: aid, name, mime, link, data: Arc::new(bytes), pixels: Some((pw, ph)) }));
|
||||
d.assets.insert(aid, Arc::new(Asset { page: pdf_page, id: aid, name, mime, link, data: Arc::new(bytes), pixels: Some((pw, ph)) }));
|
||||
let (w, h) = match want_w {
|
||||
Some(w) => (w, w * nh / nw),
|
||||
None => {
|
||||
@@ -316,8 +332,10 @@ fn place_load(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let aid = s.edit(|d, _| {
|
||||
let aid = AssetId(d.alloc());
|
||||
let mime = designcraft_render::image_mime(&bytes).to_string();
|
||||
d.assets
|
||||
.insert(aid, Arc::new(Asset { id: aid, name: name.clone(), mime, link: link.clone(), data: Arc::new(bytes), pixels: Some((pw, ph)) }));
|
||||
d.assets.insert(
|
||||
aid,
|
||||
Arc::new(Asset { page: 0, id: aid, name: name.clone(), mime, link: link.clone(), data: Arc::new(bytes), pixels: Some((pw, ph)) }),
|
||||
);
|
||||
Ok(aid)
|
||||
})?;
|
||||
s.loaded = Some((aid, (pw as f64, ph as f64)));
|
||||
|
||||
@@ -290,3 +290,39 @@ mod reflow_tests {
|
||||
assert_eq!(s.doc().unwrap().doc.page_count(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod pdf_page_tests {
|
||||
use serde_json::json;
|
||||
|
||||
use crate::Session;
|
||||
|
||||
#[test]
|
||||
fn place_a_chosen_pdf_page() {
|
||||
let dir = std::env::temp_dir().join(format!("dc-pdf-page-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let src = dir.join("two.pdf");
|
||||
// Page 1 white, page 2 a different size with a black square.
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"pages": 2, "facingPages": false, "width": 200, "height": 100})).unwrap();
|
||||
s.execute("layout.pageSize", &json!({"pages": [2], "width": 300, "height": 150})).unwrap();
|
||||
let id = s.execute("frame.create", &json!({"spread": 1, "rect": [0, 0, 300, 150]})).unwrap()["id"].as_u64().unwrap();
|
||||
s.execute("object.fill", &json!({"ids": [id], "swatch": "[Black]"})).unwrap();
|
||||
s.execute("file.exportPdf", &json!({"path": src.to_string_lossy()})).unwrap();
|
||||
s.execute("file.new", &json!({})).unwrap();
|
||||
assert!(s.execute("file.place", &json!({"path": src.to_string_lossy(), "pdfPage": 3})).is_err());
|
||||
s.execute("file.place", &json!({"path": src.to_string_lossy(), "pdfPage": 2, "x": 100, "y": 100})).unwrap();
|
||||
let d = s.doc().unwrap().doc.clone();
|
||||
let it = d.spreads[0].items.last().unwrap();
|
||||
let b = it.bounds();
|
||||
assert!((b.width() - 300.0).abs() < 1.0 && (b.height() - 150.0).abs() < 1.0, "page 2's size: {b:?}");
|
||||
assert_eq!(d.assets.values().next().unwrap().page, 1);
|
||||
let mut rr = designcraft_render::Renderer::new();
|
||||
rr.threads = 0;
|
||||
let img = rr.render_page(&d, &s.cache, 0, 1.0, false, &Default::default()).unwrap();
|
||||
assert!(img.pixel(250, 170)[0] < 60, "page 2 (black) shows: {:?}", img.pixel(250, 170));
|
||||
let pdf = designcraft_pdf::export_pdf(&d, &s.cache, &Default::default()).unwrap();
|
||||
assert!(String::from_utf8_lossy(&pdf).contains("/Subtype /Form") || String::from_utf8_lossy(&pdf).contains("/Subtype/Form"));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user