mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-14 19:27:07 -04:00
Place: PDF pages and SVGs keep their exact size (#511)
file.place sized a placed PDF page (and SVG) from its size rounded to
whole points, while the export draws the page into that size: a page of
91.774 x 613.276 pt became 92 x 613 and was drawn 0.25 % wider and
0.3 % shorter, so pieces laid edge to edge left gaps of up to 1.9 pt.
A 100.4 x 50.4 pt page placed 100.4 pt wide came out 50.2 pt tall:
`Rect { x0: 0.0, y0: 0.0, x1: 100.4, y1: 50.2 }`.
The graphic's natural size is now the exact page (crop box) or SVG size;
the rounded pixel size stays for display and preflight. place.load (the
place cursor) gets the same exact size.
Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
ca343e8985
commit
3dc0a1563a
2 files changed
+32
-3
No files matched your search
@@ -313,8 +313,9 @@ fn file_place(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
.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) = eps_size.unwrap_or((pw as f64, ph as f64));
|
||||
// 72 ppi by default unless the file says otherwise; scale so it fits the page when huge. PDF pages
|
||||
// and SVGs keep their exact size: rounding it to whole points would stretch them unevenly.
|
||||
let (nw, nh) = eps_size.or_else(|| designcraft_render::natural_size(&bytes, pdf_page as usize)).unwrap_or((pw as f64, ph as f64));
|
||||
// Image Import Options › Crop to: the frame shows that box of the PDF page.
|
||||
let crop_box = match str_param(p, "pdfCrop") {
|
||||
Some(k) if designcraft_render::is_pdf(&bytes) => {
|
||||
@@ -440,6 +441,7 @@ pub(crate) fn read_source(p: &Value) -> Result<(Vec<u8>, String, Option<String>)
|
||||
fn place_load(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let (bytes, name, link) = read_source(p)?;
|
||||
let (pw, ph) = designcraft_render::image_size(&bytes).ok_or_else(|| bad("place.load", "unsupported or corrupt image"))?;
|
||||
let natural = designcraft_render::natural_size(&bytes, 0).unwrap_or((pw as f64, ph as f64));
|
||||
let aid = s.edit(|d, _| {
|
||||
let aid = AssetId(d.alloc());
|
||||
let mime = designcraft_render::image_mime(&bytes).to_string();
|
||||
@@ -449,7 +451,7 @@ fn place_load(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
);
|
||||
Ok(aid)
|
||||
})?;
|
||||
s.loaded = Some((aid, (pw as f64, ph as f64)));
|
||||
s.loaded = Some((aid, natural));
|
||||
s.set_tool("placeGun");
|
||||
Ok(json!({"asset": aid.0, "name": name, "width": pw, "height": ph}))
|
||||
}
|
||||
@@ -689,6 +691,24 @@ mod pdf_crop_tests {
|
||||
assert!((trim.width() - 612.0).abs() < 1e-3 && (trim.height() - 792.0).abs() < 1e-3, "{trim:?}");
|
||||
assert!(t.execute("file.place", &json!({"base64": b64, "name": "a.pdf", "pdfCrop": "nope"})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placed_pdf_keeps_its_exact_page_size() {
|
||||
// A 100.4 x 50.4 pt page placed 100.4 pt wide is 50.4 pt tall, not stretched to whole points.
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 100.4, "height": 50.4, "pages": 1})).unwrap();
|
||||
let b64 = s.execute("file.exportPdf", &json!({})).unwrap()["base64"].as_str().unwrap().to_string();
|
||||
let mut t = Session::new();
|
||||
t.execute("file.new", &json!({})).unwrap();
|
||||
let r = t.execute("file.place", &json!({"base64": b64, "name": "a.pdf", "width": 100.4, "x": 0, "y": 0})).unwrap();
|
||||
let id = designcraft_doc::ItemId(r["id"].as_u64().unwrap());
|
||||
let d = &t.doc().unwrap().doc;
|
||||
let it = d.item(id).unwrap();
|
||||
let b = it.bounds();
|
||||
assert!((b.width() - 100.4).abs() < 1e-3 && (b.height() - 50.4).abs() < 1e-3, "{b:?}");
|
||||
let designcraft_doc::Content::Graphic(g) = &it.content else { panic!("not a graphic") };
|
||||
assert!((g.size.0 - 100.4).abs() < 1e-3 && (g.size.1 - 50.4).abs() < 1e-3, "{:?}", g.size);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1022,6 +1022,15 @@ pub fn image_size(bytes: &[u8]) -> Option<(u32, u32)> {
|
||||
designcraft_images::pixel_size(bytes)
|
||||
}
|
||||
|
||||
/// Natural size of a placed graphic, unrounded: a PDF page (crop box) and an SVG in points, a raster
|
||||
/// in pixels. Placed graphics are drawn at this size; `image_size` rounds it to whole pixels for display.
|
||||
pub fn natural_size(bytes: &[u8], page: usize) -> Option<(f64, f64)> {
|
||||
if is_pdf(bytes) {
|
||||
return pdf_page_size(bytes, page).map(|(w, h)| (w.max(1e-3), h.max(1e-3)));
|
||||
}
|
||||
designcraft_images::natural_size(bytes)
|
||||
}
|
||||
|
||||
/// MIME type guess for encoded image bytes.
|
||||
/// Is this a PDF file (placed PDFs are graphics)?
|
||||
pub fn is_pdf(bytes: &[u8]) -> bool {
|
||||
|
||||
Reference in new issue
Block a user