.ai import: a file whose PDF part can't be read opens from its editing data

Squashed from #5051 onto main.
This commit is contained in:
dotCooCoo
2026-11-02 12:04:04 +07:00
committed by bflatastic
parent 54436b0aa2
commit 100730d4d3
43 changed files with 476 additions and 42 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ use graphics::{GState, Out};
use interp::{Fault, Interp};
use obj::PsError;
pub use native::{ai_alone, is_loss, layered_ai};
pub use native::{ai_alone, ai_alone_on_page, is_loss, layered_ai};
pub use text::family_style;
/// The page of a PostScript file without a bounding box (US Letter).
+1 -1
View File
@@ -25,7 +25,7 @@ pub(super) const MAX_PIXELS: u64 = 1 << 25;
pub(super) const MAX_PLACED_ELSEWHERE: usize = 4096;
/// The note for an image past [`MAX_PIXELS`]: the file then opens from its PDF part, which reads
/// larger images (#1479).
/// larger images (#1479), unless that part can't be read either; then the image is left out.
pub const IMAGE_TOO_LARGE: &str = "an image too large to read from the editing data was left out";
/// Most hex digits of an image's samples read from comment lines (CMYK and alpha at the largest size).
+1 -1
View File
@@ -51,7 +51,7 @@ use build::{Frame, MADE, prune_unseen};
use compare::{
MAX_DIFFERENCE, MAX_SOLID_DIFFERENCE, PageView, Shares, compare, compared, comparison_render, comparison_samples, differ, differ_in_colour,
};
pub use layers::{ai_alone, layered_ai};
pub use layers::{ai_alone, ai_alone_on_page, layered_ai};
pub(super) use layers::{layered, program_alone};
pub use slots::is_loss;
#[cfg(test)]
+21
View File
@@ -53,6 +53,27 @@ pub(super) fn build(
build_from(ai::read(data, matches!(page, Page::Art))?, data, visible, page, outlined, trust, unit)
}
/// [`build`] without a page, for a `.ai` whose PDF part can't be read, its first page `page`
/// (width and height, either way round) points large in units of `unit` points: the document is
/// drawn `unit` times as large when its first artboard, or its art's box, has the page's size in
/// those units and not in points, as [`build_from`] decides with a page.
pub(super) fn build_on_page(data: &[u8], page: (f64, f64), unit: f64) -> Result<(Document, Vec<String>), String> {
let structure = ai::read(data, false)?;
let board = structure.artboard.map(|a| structure.to_doc.transform_rect_bbox(a));
let art = structure.bbox.map(|[a, b, c, d]| structure.to_doc.transform_rect_bbox(Rect::new(a, b, c, d)));
let (w, h) = page;
let fits = |r: Option<Rect>, k: f64| {
r.is_some_and(|r| [(w, h), (h, w)].iter().any(|(a, b)| (r.width() - a / k).abs() <= 0.5 && (r.height() - b / k).abs() <= 0.5))
};
let in_units = |r: Option<Rect>| unit.is_finite() && unit > 1.0 && fits(r, unit) && !fits(r, 1.0);
let scale = in_units(board) || (!fits(board, 1.0) && in_units(art));
let (mut doc, notes) = build_from(structure, data, None, Page::Artboard, false, false, 1.0)?;
if scale {
scale_document(&mut doc, unit);
}
Ok((doc, notes))
}
/// [`build`] from `structure`, the editing copy `data` already read.
pub(super) fn build_from(
structure: Structure,
+11 -1
View File
@@ -1,4 +1,4 @@
use super::build::{PAGE_MOVE, Page, build, build_from};
use super::build::{PAGE_MOVE, Page, build, build_from, build_on_page};
use super::slots::LAYERS_UNREAD;
use super::{Affine, Arc, Cow, Document, Imported, Node, NodeKind, PathData, Rect, Vec2, ai};
@@ -216,6 +216,16 @@ pub fn ai_alone(private: &[u8]) -> Result<(Document, Vec<String>), String> {
build(&data, None, Page::Artboard, false, false, 1.0)
}
/// [`ai_alone`] for a `.ai` whose PDF part can't be read, its first page `page` (width and height)
/// points large in units of `unit` points (its `UserUnit`): editing data whose first artboard, or
/// whose art's box, has the page's size in those units, and not in points, comes in `unit` times as
/// large, as [`layered_ai`] reads it. A page turned a quarter (`/Rotate 90`) is measured either
/// way round.
pub fn ai_alone_on_page(private: &[u8], page: (f64, f64), unit: f64) -> Result<(Document, Vec<String>), String> {
let data = ai::decode_private(private)?;
build_on_page(&data, page, unit)
}
/// Does the editing data (or a program in the legacy format) have layers?
fn has_layers(data: &[u8]) -> bool {
data.windows(15).any(|w| w == b"%AI5_BeginLayer")
+3 -1
View File
@@ -37,7 +37,9 @@ mod ps;
mod scene;
mod tiff;
pub use import::{Imported, POSTSCRIPT_ERRORS, ai_alone, family_style, import, import_trusting, import_with, is_loss, layered_ai, page};
pub use import::{
Imported, POSTSCRIPT_ERRORS, ai_alone, ai_alone_on_page, family_style, import, import_trusting, import_with, is_loss, layered_ai, page,
};
pub use legacy_ai::{HiddenObjects, LegacyAiOptions, LegacyAiOutput, export_legacy_ai};
pub use print::{PrintJob, PrintPage, print};