mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-01 22:26:49 -06:00
feat(pptx): export documents as editable PowerPoint presentations
New designcraft-pptx crate (L3) and file.exportPptx command (File › Export PowerPoint…, CLI --export x.pptx). Each page becomes a slide of native objects: shapes with fills, gradients, strokes and effects, pictures cropped like their frames, native tables, and text boxes. Text keeps DesignCraft's composition by default: composed lines end in line breaks, justified spacing becomes character spacing, and leading becomes percentage line spacing (PowerPoint rounds exact spacing to whole points; its 100% line is 1.2 em for every font). Threaded stories split at their frames and wrapped lines get their own boxes. lineBreaks: reflow writes one box per frame in native columns instead. TrueType fonts are embedded as Embedded OpenType when their licences allow. Objects PowerPoint can't represent (placed PDF/EPS, type on a path, some effects) are rasterised through the existing rasterize_where path. Checked in PowerPoint for Mac and LibreOffice: on the sample magazine, word positions match DesignCraft's PDF to within 0.8 pt horizontally and 1.6 pt vertically.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//! Headless DesignCraft.
|
||||
//!
|
||||
//! ```text
|
||||
//! designcraft-cli run [--in FILE.designcraft|FILE.idml | --sample] [--cmd ID[=JSON]]... [--page N] [--scale S] [--export OUT.png|.jpg|.pdf|.designcraft|.idml|.epub] [--pdf-options JSON] [--all-pages DIR]
|
||||
//! designcraft-cli run [--in FILE.designcraft|FILE.idml | --sample] [--cmd ID[=JSON]]... [--page N] [--scale S] [--export OUT.png|.jpg|.pdf|.designcraft|.idml|.epub|.pptx] [--pdf-options JSON] [--all-pages DIR]
|
||||
//! designcraft-cli commands [FILTER] # list commands (JSON), optionally only ids/labels/menus containing FILTER
|
||||
//! designcraft-cli describe ID # one command: label, menu, shortcut, parameters
|
||||
//! designcraft-cli script [FILE|-] [--in FILE | --sample] [--connect PORT] [--save OUT] [--export OUT] [--keep-going]
|
||||
@@ -209,6 +209,14 @@ fn run(args: &[String]) -> Result<(), String> {
|
||||
}
|
||||
|
||||
fn export(s: &mut Session, out: &str, page: usize, scale: f64) -> Result<(), String> {
|
||||
if out.to_ascii_lowercase().ends_with(".pptx") {
|
||||
let r = s.execute("file.exportPptx", &json!({"path": out})).map_err(|e| e.to_string())?;
|
||||
eprintln!("wrote {out} ({} slides, {} bytes)", r["slides"], r["bytes"]);
|
||||
for w in r["warnings"].as_array().into_iter().flatten() {
|
||||
eprintln!("warning: {}", w.as_str().unwrap_or_default());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if out.to_ascii_lowercase().ends_with(".idml") {
|
||||
let r = s.execute("file.exportIdml", &json!({"path": out})).map_err(|e| e.to_string())?;
|
||||
eprintln!("wrote {out} ({} bytes)", r["bytes"]);
|
||||
@@ -332,6 +340,8 @@ fn script(args: &[String]) -> Result<(), String> {
|
||||
exec(b, "file.exportIdml", p)?;
|
||||
} else if lower.ends_with(".epub") {
|
||||
exec(b, "file.exportEpub", p)?;
|
||||
} else if lower.ends_with(".pptx") {
|
||||
exec(b, "file.exportPptx", p)?;
|
||||
} else {
|
||||
// PNG / JPEG of the first page.
|
||||
b.call("app.export", p)?;
|
||||
|
||||
Reference in New Issue
Block a user