fix/加入兼容层处理不支持的扩展组码-qdj-0429

qdj
邱德佳 1 week ago
parent f2d9fb75c7
commit 5dd4d232e7

@ -0,0 +1,209 @@
use anyhow::{Context, Result};
use dxf::Drawing;
use std::io::Write;
use std::path::Path;
use tempfile::NamedTempFile;
use tracing::warn;
struct SanitizedDxf {
bytes: Vec<u8>,
removed_pairs: usize,
}
pub fn load_file(path: &Path) -> Result<Drawing> {
match Drawing::load_file(path) {
Ok(drawing) => Ok(drawing),
Err(original_error) => {
let Some(sanitized) = sanitize_file(path)? else {
return Err(original_error.into());
};
warn!(
path = %path.display(),
removed_pairs = sanitized.removed_pairs,
original_error = %original_error,
"retrying DXF load after removing unsupported XRECORD group codes"
);
let mut temp_file =
NamedTempFile::new().context("Could not create sanitized DXF temporary file")?;
temp_file
.write_all(&sanitized.bytes)
.context("Could not write sanitized DXF temporary file")?;
Drawing::load_file(temp_file.path()).with_context(|| {
format!(
"Failed to load sanitized DXF copy after removing {} unsupported XRECORD group code pair(s). Original error: {original_error}",
sanitized.removed_pairs
)
})
}
}
}
fn sanitize_file(path: &Path) -> Result<Option<SanitizedDxf>> {
let bytes =
std::fs::read(path).with_context(|| format!("Could not read {}", path.display()))?;
Ok(sanitize_unsupported_xrecord_pairs(&bytes))
}
fn sanitize_unsupported_xrecord_pairs(bytes: &[u8]) -> Option<SanitizedDxf> {
let lines = split_lines_preserving_endings(bytes);
let mut output = Vec::with_capacity(bytes.len());
let mut removed_pairs = 0;
let mut in_objects_section = false;
let mut waiting_for_section_name = false;
let mut in_xrecord = false;
let mut index = 0;
while index < lines.len() {
let code_line = lines[index];
let Some(value_line) = lines.get(index + 1).copied() else {
output.extend_from_slice(code_line);
break;
};
let code = parse_group_code(code_line);
let skip_pair = code
.is_some_and(|code| in_objects_section && in_xrecord && !is_supported_group_code(code));
if skip_pair {
removed_pairs += 1;
} else {
output.extend_from_slice(code_line);
output.extend_from_slice(value_line);
}
if let Some(code) = code {
update_section_state(
code,
trimmed_ascii(value_line),
&mut in_objects_section,
&mut waiting_for_section_name,
&mut in_xrecord,
);
}
index += 2;
}
(removed_pairs > 0).then_some(SanitizedDxf {
bytes: output,
removed_pairs,
})
}
fn update_section_state(
code: i32,
value: &[u8],
in_objects_section: &mut bool,
waiting_for_section_name: &mut bool,
in_xrecord: &mut bool,
) {
if *waiting_for_section_name {
if code == 2 {
*in_objects_section = value.eq_ignore_ascii_case(b"OBJECTS");
*in_xrecord = false;
}
*waiting_for_section_name = false;
}
if code != 0 {
return;
}
if value.eq_ignore_ascii_case(b"SECTION") {
*waiting_for_section_name = true;
*in_xrecord = false;
} else if value.eq_ignore_ascii_case(b"ENDSEC") {
*in_objects_section = false;
*waiting_for_section_name = false;
*in_xrecord = false;
} else if *in_objects_section {
*in_xrecord = value.eq_ignore_ascii_case(b"XRECORD");
} else {
*in_xrecord = false;
}
}
fn is_supported_group_code(code: i32) -> bool {
matches!(
code,
0..=79
| 90..=102
| 105
| 110..=149
| 160..=179
| 210..=239
| 270..=481
| 999
| 1000..=1071
)
}
fn parse_group_code(line: &[u8]) -> Option<i32> {
std::str::from_utf8(trimmed_ascii(line))
.ok()?
.parse::<i32>()
.ok()
}
fn trimmed_ascii(line: &[u8]) -> &[u8] {
let mut start = 0;
let mut end = line.len();
while start < end && line[start].is_ascii_whitespace() {
start += 1;
}
while start < end && line[end - 1].is_ascii_whitespace() {
end -= 1;
}
&line[start..end]
}
fn split_lines_preserving_endings(bytes: &[u8]) -> Vec<&[u8]> {
let mut lines = Vec::new();
let mut start = 0;
for (index, byte) in bytes.iter().enumerate() {
if *byte == b'\n' {
lines.push(&bytes[start..=index]);
start = index + 1;
}
}
if start < bytes.len() {
lines.push(&bytes[start..]);
}
lines
}
#[cfg(test)]
mod tests {
use super::sanitize_unsupported_xrecord_pairs;
#[test]
fn removes_unsupported_group_codes_only_from_object_xrecords() {
let input = b" 0\r\nSECTION\r\n 2\r\nOBJECTS\r\n 0\r\nXRECORD\r\n100\r\nAcDbXrecord\r\n280\r\n 1\r\n5005\r\nMETRIC\r\n1070\r\n 0\r\n 0\r\nENDSEC\r\n 0\r\nEOF\r\n";
let sanitized = sanitize_unsupported_xrecord_pairs(input).expect("expected cleanup");
let output = String::from_utf8(sanitized.bytes).expect("expected ASCII test data");
assert_eq!(1, sanitized.removed_pairs);
assert!(!output.contains("5005"));
assert!(output.contains("XRECORD"));
assert!(output.contains("1070\r\n 0"));
}
#[test]
fn leaves_unsupported_group_codes_outside_object_xrecords_unchanged() {
let input =
b" 0\nSECTION\n 2\nENTITIES\n 0\nXRECORD\n5005\nMETRIC\n 0\nENDSEC\n 0\nEOF\n";
assert!(sanitize_unsupported_xrecord_pairs(input).is_none());
}
}

@ -25,6 +25,7 @@ use tracing_subscriber::prelude::*;
#[cfg(feature = "venator")]
use venator::Venator;
mod dxf_loader;
mod qelmt;
#[derive(Parser, Debug)]
@ -109,7 +110,7 @@ fn main() -> Result<()> {
.unwrap_or_else(|| file_name.as_os_str())
.to_string_lossy();
// 加载dxf文件获取dxf解析对象drawing
let drawing: Drawing = Drawing::load_file(&file_name).context(format!(
let drawing: Drawing = dxf_loader::load_file(&file_name).context(format!(
"Failed to load {friendly_file_name}...\n\tMake sure the file is a valid .dxf file.",
))?;
// 传入文件名、步长、dxf解析对象drawing创建elmt的definition标签

Loading…
Cancel
Save