Merge pull request #1528 from fortesnow/ai-off-page-art-overlap
.ai import: a page's art past its artboard no longer lies over the page before it
This commit is contained in:
commit
afda1cd846
|
|
@ -197,6 +197,8 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
|
|||
b.colors.uncalibrated_rgb = opts.uncalibrated_rgb && picked.iter().filter_map(|&n| pages.get(n)).any(crate::pages::has_private_data);
|
||||
let cache = InterpreterCache::new();
|
||||
let mut x = 0.0;
|
||||
// Where the next page's art may begin (a `.ai` page's art reaches past its artboard).
|
||||
let mut clear = f64::NEG_INFINITY;
|
||||
// Every page only text over private data: a file saved without its PDF part shows a notice
|
||||
// there, not its art. A page that states where its art is isn't that notice.
|
||||
let (mut only_text, mut art_boxes) = (true, false);
|
||||
|
|
@ -220,6 +222,8 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
|
|||
let xf = Affine::translate((x - frame.x0, -frame.y0)) * init;
|
||||
let mut ctx = Context::new(xf, ab, &cache, pdf.xref(), settings.clone());
|
||||
b.page = ab;
|
||||
// The first id of this page's art.
|
||||
let first_id = b.next;
|
||||
let scan = scan_page(page, &mut ocgs, all_on, &mut b.fonts);
|
||||
if route {
|
||||
let mut seen = HashSet::new();
|
||||
|
|
@ -267,6 +271,33 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
|
|||
if clipped {
|
||||
b.warn(PAST_PAGE_NOTE);
|
||||
}
|
||||
// A `.ai` page keeps its art past the artboard edge, unclipped (#646), so a page whose art
|
||||
// reaches far to the left would lay it over the page before it in the row: there it would
|
||||
// be taken for that page's art (by the check against the editing data, which then finds
|
||||
// art its layers don't have there, and by `place_pages`). Such a page moves right until its
|
||||
// art clears the art before it.
|
||||
let mut art_right = f64::NEG_INFINITY;
|
||||
if ai && let Some(art) = parts.iter().flat_map(|(_, v)| v.iter()).filter_map(|n| n.visual_bounds()).reduce(|a, b| a.union(b)) {
|
||||
// Art at a coordinate that isn't a number (a damaged file) moves nothing.
|
||||
let dx = Some((clear - art.x0).max(0.0)).filter(|dx| dx.is_finite()).unwrap_or(0.0);
|
||||
if dx > 0.0 {
|
||||
let shift = Affine::translate((dx, 0.0));
|
||||
for (_, v) in &mut parts {
|
||||
for n in v.iter_mut() {
|
||||
Arc::make_mut(n).transform(shift, false);
|
||||
}
|
||||
}
|
||||
// What the page's lines of type were is told where their glyphs are (the outlines
|
||||
// stroked on them are looked for there), so that moves with them.
|
||||
for facts in b.lines.iter_mut().filter(|(id, _)| id.0 >= first_id).map(|(_, facts)| facts) {
|
||||
for ink in &mut facts.ink {
|
||||
*ink = *ink + kurbo::Vec2::new(dx, 0.0);
|
||||
}
|
||||
}
|
||||
ab = Rect::new(ab.x0 + dx, ab.y0, ab.x1 + dx, ab.y1);
|
||||
}
|
||||
art_right = art.x1 + dx;
|
||||
}
|
||||
let children: Vec<Arc<Node>> = parts.iter().flat_map(|(_, v)| v.iter().cloned()).collect();
|
||||
only_text &= ai && is_only_text(&children);
|
||||
art_boxes |= crate::pages::states_art(page);
|
||||
|
|
@ -278,6 +309,7 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
|
|||
ab = art;
|
||||
}
|
||||
x = right + opts.artboard_gap;
|
||||
clear = right.max(art_right) + opts.artboard_gap;
|
||||
doc.artboards.push(Artboard {
|
||||
id: i as u32 + 1,
|
||||
name: format!("Artboard {}", i + 1),
|
||||
|
|
|
|||
|
|
@ -131,6 +131,28 @@ fn a_stroke_written_as_stroked_glyph_outlines_is_the_types_stroke() {
|
|||
assert_eq!(d.layers[0].children().unwrap().len(), 2);
|
||||
}
|
||||
|
||||
/// A `.ai` page moved right in the row, so its art past the artboard clears the page before it
|
||||
/// (#1528), still has the stroke drawn on its type's glyphs read as the type's stroke.
|
||||
#[test]
|
||||
fn a_stroke_on_the_type_of_a_moved_ai_page_is_still_the_types_stroke() {
|
||||
let ai = "/PieceInfo << /Illustrator << /Private << /AIPrivateData1 7 /NumBlock 1 >> >> >>";
|
||||
let content = format!(
|
||||
"0 g -70 10 80 20 re f BT 1 0 0 rg /F1 24 Tf 10 50 Td (Hi) Tj ET 0 0 1 RG 0.5 w {}{}",
|
||||
outline('H', 10.0),
|
||||
outline('i', 10.0 + 0.722 * 24.0)
|
||||
);
|
||||
let pages = [("0 g 10 10 20 20 re f", String::new()), (content.as_str(), HELVETICA.to_string())].map(|(c, resources)| PdfPage {
|
||||
entries: ai.into(),
|
||||
resources,
|
||||
..PdfPage::new(100.0, 100.0, c)
|
||||
});
|
||||
let d = import(&vectorcraft_testkit::pdf::pdf(&pages, None)).unwrap();
|
||||
assert!(d.artboards[1].rect.x0 > d.artboards[0].rect.x1 + 36.0 + 1.0, "page 2 moved: {:?}", d.artboards);
|
||||
let art = d.layers[1].children().unwrap();
|
||||
assert_eq!(art.len(), 2, "the note and the type: {art:?}");
|
||||
assert!(matches!(art[1].appearance.items.as_slice(), [AppearanceItem::Stroke(_)]), "{:?}", art[1].appearance);
|
||||
}
|
||||
|
||||
/// A 300 × 200 pt page with `lines` of (x, y from the top, size, text) in Source Sans 3.
|
||||
fn source_sans(lines: &[(f32, f32, f32, &str)]) -> Vec<u8> {
|
||||
crate::tests_live_text::text_pdf(lines, false)
|
||||
|
|
|
|||
|
|
@ -158,6 +158,27 @@ fn art_of_neighbouring_artboards_off_a_page_is_left_to_its_own_page() {
|
|||
assert_eq!(d.layers[0].children().map_or(0, |c| c.len()), 2);
|
||||
}
|
||||
|
||||
/// A `.ai` keeps its art past the artboard edge (#646): a note mostly left of artboard 2 (as notes
|
||||
/// for a design are put beside it) doesn't lie over artboard 1 in the row, where it would be taken
|
||||
/// for that page's art: page 2 moves right until its art clears page 1's.
|
||||
#[test]
|
||||
fn art_of_an_ai_page_reaching_left_does_not_lie_over_the_page_before() {
|
||||
let ai = "/PieceInfo << /Illustrator << /Private << /AIPrivateData1 7 /NumBlock 1 >> >> >>";
|
||||
let page = |content: &str| PdfPage { entries: ai.into(), ..PdfPage::new(100.0, 100.0, content) };
|
||||
// Page 2's note runs from 70 points left of its box to 10 points into it.
|
||||
let pages = [page("0 g 10 10 20 20 re f"), page("1 1 1 rg 1 0 0 RG -70 10 80 60 re B")];
|
||||
let d = open(&pdf(&pages, None), ImportOptions::default());
|
||||
let (a, b) = (d.artboards[0].rect, d.artboards[1].rect);
|
||||
let note = d.layers[1].children().unwrap()[0].visual_bounds().unwrap();
|
||||
assert!(note.x0 >= a.x1 + 36.0, "the note clears artboard 1: {note:?} {a:?}");
|
||||
assert!((b.x0 - note.x0 - 70.0).abs() < 1.0, "and keeps its place by artboard 2: {note:?} {b:?}");
|
||||
assert_eq!((b.width(), b.height()), (100.0, 100.0));
|
||||
// A plain PDF clips its art to the page: its pages stay 36 points apart.
|
||||
let plain = [PdfPage::new(100.0, 100.0, "0 g 10 10 20 20 re f"), PdfPage::new(100.0, 100.0, "1 0 0 RG -70 10 80 60 re S")];
|
||||
let d = open(&pdf(&plain, None), ImportOptions::default());
|
||||
assert_eq!(d.artboards[1].rect.x0, d.artboards[0].rect.x1 + 36.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_encrypted_pdf_opens_with_its_password_only() {
|
||||
let bytes = pdf(&[PdfPage::new(200.0, 100.0, "1 0 0 rg 10 10 50 50 re f")], Some("secret"));
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue