mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 03:37:00 -06:00
EPS import: a commented copy of a text object that has a plain one isn't another object
Some files write a text object twice, once plainly and once as comments (and others only as comments). Reading both made a second copy of the type, built from the text document, over the page's own (wrong size and place). The commented copy is now skipped when its story has a plain text object. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
ec43ac5a1e
commit
618b1cf94f
@@ -266,6 +266,16 @@ fn unzstd(packed: &[u8]) -> Option<Vec<u8>> {
|
||||
(data.len() as u64 != MAX_DECODED).then_some(data)
|
||||
}
|
||||
|
||||
/// The stories that text objects written plainly (not as comments) name.
|
||||
fn plain_stories(data: &[u8]) -> BTreeSet<u32> {
|
||||
data.split(|b| matches!(b, b'\r' | b'\n'))
|
||||
.filter_map(|raw| {
|
||||
let line = String::from_utf8_lossy(raw);
|
||||
line.trim().strip_suffix("/StoryIndex ,").and_then(|n| n.trim().parse().ok())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The layers of the editing copy `data`.
|
||||
fn parse(data: &[u8]) -> Option<Parsed> {
|
||||
let (mut bbox, mut hires, mut artboard, mut template) = (None, None, None, None);
|
||||
@@ -274,6 +284,10 @@ fn parse(data: &[u8]) -> Option<Parsed> {
|
||||
let mut dict_depth = 0usize;
|
||||
// In a text object's dictionary: the story it names, once the dictionary says.
|
||||
let mut text_story: Option<Option<u32>> = None;
|
||||
// Whether that text object is written as comments, which the file does for some of its stories
|
||||
// twice (once plain, once commented) and for others only commented.
|
||||
let mut text_commented = false;
|
||||
let plain = plain_stories(data);
|
||||
let mut count = 0usize;
|
||||
for raw in data.split(|b| matches!(b, b'\r' | b'\n')) {
|
||||
let line = String::from_utf8_lossy(raw);
|
||||
@@ -284,8 +298,18 @@ fn parse(data: &[u8]) -> Option<Parsed> {
|
||||
// A text object's dictionary may be written as comments (`%_`), which a reader that doesn't
|
||||
// know text objects skips: what that reader skips is read here, for the text object only.
|
||||
let line = match line.strip_prefix("%_") {
|
||||
Some(rest) if dict_depth > 0 || rest.starts_with("/AI11Text") && rest.ends_with(':') => rest.trim(),
|
||||
_ => line,
|
||||
Some(rest) if dict_depth > 0 || rest.starts_with("/AI11Text") && rest.ends_with(':') => {
|
||||
if dict_depth == 0 {
|
||||
text_commented = true;
|
||||
}
|
||||
rest.trim()
|
||||
}
|
||||
_ => {
|
||||
if dict_depth == 0 {
|
||||
text_commented = false;
|
||||
}
|
||||
line
|
||||
}
|
||||
};
|
||||
if line.starts_with('%') {
|
||||
match line {
|
||||
@@ -331,7 +355,10 @@ fn parse(data: &[u8]) -> Option<Parsed> {
|
||||
if dict_depth == 0
|
||||
&& let Some(story) = text_story.take()
|
||||
{
|
||||
layer.code.push_str(&format!("{} __txt\n", story.map_or(-1, i64::from)));
|
||||
// A commented copy of a story that has a plain text object is not another object.
|
||||
if !(text_commented && story.is_some_and(|n| plain.contains(&n))) {
|
||||
layer.code.push_str(&format!("{} __txt\n", story.map_or(-1, i64::from)));
|
||||
}
|
||||
}
|
||||
} else if line.ends_with(':') {
|
||||
dict_depth += 1;
|
||||
|
||||
@@ -436,3 +436,15 @@ fn type_of_the_page_that_no_story_places_stays_with_the_nearest_text_object() {
|
||||
});
|
||||
assert!(all.contains(&"Extra".to_string()), "{all:?} {:?}", r.warnings);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_commented_copy_of_a_story_that_has_a_plain_text_object_is_not_another_object() {
|
||||
// The file writes some stories twice (plain, then as comments) and others only as comments.
|
||||
let doc = text_document(&[("One\r", 0, (8200.0, 8180.0), (0.0, 0.0), 0.0), ("Two\r", 0, (8300.0, 8180.0), (0.0, 0.0), 0.0)]);
|
||||
let commented = |story| -> String { text_object(story).lines().map(|l| format!("%_{l}\n")).collect() };
|
||||
let body = text_object(0) + &commented(0) + &commented(1);
|
||||
let art = native_with_text(&layer("Spare", false, &body), &doc);
|
||||
let r = import(&eps(&page_square(10, 10), &art)).unwrap();
|
||||
let texts: Vec<String> = text_of(&r.document.layers[0]).iter().map(|t| t.plain_text()).collect();
|
||||
assert_eq!(texts, ["One", "Two"], "{:?}", r.warnings);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user