mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 14:41:52 +00:00
HP-GL: a .plt file opens when its first instruction is SI, DI or another known one
A file read as HP-GL by its extension no longer has to start with an instruction plot files usually start with. Any instruction the reader knows, or one of another set that a semicolon ends, may come first; the rest of the check still refuses files that are not HP-GL. Telling HP-GL apart by content alone keeps the stricter check.
This commit is contained in:
+11
-3
@@ -66,6 +66,13 @@ const FIRST: &[[u8; 2]] = &[
|
||||
/// WD, DT, SM and PE. An instruction the reader doesn't know counts when its numbers end with a
|
||||
/// semicolon.
|
||||
pub fn is_hpgl(bytes: &[u8]) -> bool {
|
||||
holds_instructions(bytes, true)
|
||||
}
|
||||
|
||||
/// Does `bytes` hold HP-GL instructions as [`is_hpgl`] checks? When `strict` is false, any of them
|
||||
/// may come first: a file picked by its extension may start with one a plot file seldom starts
|
||||
/// with (`SI`, `DI`).
|
||||
fn holds_instructions(bytes: &[u8], strict: bool) -> bool {
|
||||
let mut lex = Lexer::new(bytes);
|
||||
let mut terminator = lex::ETX;
|
||||
let mut first = 0;
|
||||
@@ -77,7 +84,7 @@ pub fn is_hpgl(bytes: &[u8]) -> bool {
|
||||
if n == 0 {
|
||||
first = lex.pos();
|
||||
}
|
||||
if n == 0 && !FIRST.contains(&op) {
|
||||
if strict && n == 0 && !FIRST.contains(&op) {
|
||||
return false;
|
||||
}
|
||||
let known = PLAYED.contains(&op) || IGNORED.contains(&op);
|
||||
@@ -110,9 +117,10 @@ pub fn is_hpgl(bytes: &[u8]) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Read an HP-GL file.
|
||||
/// Read an HP-GL file. It may start with any instruction the reader knows, or one of another set
|
||||
/// that a semicolon ends; the rest of [`is_hpgl`]'s check applies.
|
||||
pub fn import(bytes: &[u8]) -> Result<Imported, String> {
|
||||
if !is_hpgl(bytes) {
|
||||
if !holds_instructions(bytes, false) {
|
||||
return Err("not an HP-GL file".into());
|
||||
}
|
||||
let mut lex = Lexer::new(bytes);
|
||||
|
||||
@@ -440,6 +440,19 @@ fn only_plot_files_are_taken_for_hpgl() {
|
||||
assert!(is_hpgl(b"IN;SP1;PU;PD;PU;PD;PU;PD;hello world"));
|
||||
}
|
||||
|
||||
/// A file read as HP-GL (picked by its extension) may start with any instruction the reader knows,
|
||||
/// or one of another set that a semicolon ends; its content alone doesn't make it HP-GL.
|
||||
#[test]
|
||||
fn a_plot_file_may_start_with_any_instruction() {
|
||||
for f in [&b"SI0.2,0.3;SP1;PA0,0;PD1016,0;"[..], b"DI1,0;SP1;PA0,0;PD1016,0;", b"GC5;SP1;PA0,0;PD1016,0;"] {
|
||||
assert!(!is_hpgl(f), "{:?}", String::from_utf8_lossy(f));
|
||||
assert_eq!(leaves(&plot_bytes(f).document).len(), 1, "{:?}", String::from_utf8_lossy(f));
|
||||
}
|
||||
for f in [&b"GC5"[..], b"SIhello world", b"set terminal png\n"] {
|
||||
assert_eq!(import(f).err().as_deref(), Some("not an HP-GL file"), "{:?}", String::from_utf8_lossy(f));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escapes_that_carry_values_or_data_are_skipped_whole() {
|
||||
// A reset leaves HP-GL until a language switch; symbol-set escapes carry a value and a letter.
|
||||
|
||||
@@ -79,6 +79,16 @@ fn files_that_are_not_hpgl_are_refused() {
|
||||
assert_eq!(text["format"], "txt", "{text}");
|
||||
}
|
||||
|
||||
/// A plot file named as one opens when its first instruction is one a plot file seldom starts with;
|
||||
/// under another name it isn't taken for HP-GL.
|
||||
#[test]
|
||||
fn a_plot_file_by_its_name_may_start_with_any_instruction() {
|
||||
let plot = b"SI0.2,0.3;SP1;PA0,0;PD1016,0;";
|
||||
let r = Session::new().execute("document.open", &json!({"name": "part.plt", "dataBase64": b64(plot)})).unwrap();
|
||||
assert_eq!(r["format"].as_str(), Some("hpgl"), "{r}");
|
||||
assert!(detect("drawing.prn", plot).is_none_or(|f| f.id != "hpgl"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_dialogs_and_the_format_list_name_hpgl() {
|
||||
let exts = ["plt", "hpgl", "hgl", "hpg"];
|
||||
|
||||
Reference in New Issue
Block a user