mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-31 06:16:51 -06:00
DXF: keep files that declare ASCII or UTF8 pure ASCII when saving
A pre-2007 file whose header says $DWGCODEPAGE ASCII (or UTF8/UNICODE, which no pre-2007 reader expects) is still written with \U+ escapes only, instead of Windows-1252 (or UTF-8) bytes the header doesn't declare. Review of #1693. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
28fe95fff0
commit
936f08db1f
@@ -69,12 +69,24 @@ pub fn decode_code_page(bytes: &[u8], code_page: &str) -> Option<String> {
|
||||
decoder(code_page).map(|d| decode(bytes, d).into_owned())
|
||||
}
|
||||
|
||||
/// The code page a pre-2007 file's text is written in, or `None` for pure ASCII with escapes:
|
||||
/// when CADCraft doesn't know `code_page`, and for `ASCII` (the reader takes such files as
|
||||
/// Windows-1252, but a writer must keep them ASCII) and `UTF8`/`UNICODE`, which no pre-2007
|
||||
/// reader expects.
|
||||
pub(crate) fn encoder(code_page: &str) -> Option<Decoder> {
|
||||
if code_page.trim().eq_ignore_ascii_case("ASCII") {
|
||||
return None;
|
||||
}
|
||||
decoder(code_page).filter(|d| !matches!(d, Decoder::Utf8))
|
||||
}
|
||||
|
||||
/// Append the bytes of the non-ASCII character `c` in the code page `d`, and return true, when
|
||||
/// the code page has it; otherwise append nothing and return false (the caller escapes it).
|
||||
pub(crate) fn encode_char(c: char, d: Decoder, out: &mut Vec<u8>) -> bool {
|
||||
let mut buf = [0; 4];
|
||||
match d {
|
||||
Decoder::Utf8 => out.extend_from_slice(c.encode_utf8(&mut buf).as_bytes()),
|
||||
// Not a pre-2007 code page ([`encoder`] never gives it): escaped.
|
||||
Decoder::Utf8 => return false,
|
||||
Decoder::Encoding(e) => {
|
||||
let (bytes, _, unmappable) = e.encode(c.encode_utf8(&mut buf));
|
||||
// A code page byte that is ASCII would read back as another character, and a NUL,
|
||||
|
||||
@@ -418,9 +418,10 @@ pub fn write_ascii_as(tags: &[Tag], utf8: bool) -> String {
|
||||
/// Write tags as pre-R2007 ASCII DXF with text in the code page `code_page` names (a
|
||||
/// `$DWGCODEPAGE` value such as `ANSI_1252`), as the DXF Reference describes for those versions:
|
||||
/// a character the code page has is written as its bytes, any other as a `\U+XXXX` escape. A
|
||||
/// code page CADCraft doesn't know gives [`write_ascii`]'s pure ASCII output.
|
||||
/// code page CADCraft doesn't know, `ASCII`, and `UTF8`/`UNICODE` (not a pre-2007 code page)
|
||||
/// give [`write_ascii`]'s pure ASCII output.
|
||||
pub fn write_ascii_in(tags: &[Tag], code_page: &str) -> Vec<u8> {
|
||||
ascii(tags, Strings::Escaped(codepage::decoder(code_page)))
|
||||
ascii(tags, Strings::Escaped(codepage::encoder(code_page)))
|
||||
}
|
||||
|
||||
/// How a writer stores string values.
|
||||
@@ -463,7 +464,7 @@ pub fn write_binary(tags: &[Tag], utf8: bool) -> Vec<u8> {
|
||||
/// Write tags as pre-R2007 binary DXF with text in the code page `code_page` names, as
|
||||
/// [`write_ascii_in`] does.
|
||||
pub fn write_binary_in(tags: &[Tag], code_page: &str) -> Vec<u8> {
|
||||
binary(tags, Strings::Escaped(codepage::decoder(code_page)))
|
||||
binary(tags, Strings::Escaped(codepage::encoder(code_page)))
|
||||
}
|
||||
|
||||
fn binary(tags: &[Tag], strings: Strings) -> Vec<u8> {
|
||||
|
||||
@@ -46,12 +46,16 @@ fn text_is_written_in_the_code_page() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Without a known code page the output is what `write_ascii` writes, line breaks included.
|
||||
/// Without a known code page, and for `ASCII`, `UTF8` and `UNICODE` headers, the output is what
|
||||
/// `write_ascii` writes (pure ASCII with escapes), line breaks included.
|
||||
#[test]
|
||||
fn unknown_code_page_is_pure_ascii() {
|
||||
let tags = file("EBCDIC", "Ação\r\nfim");
|
||||
let bytes = write_ascii_in(&tags, "EBCDIC");
|
||||
assert!(bytes.is_ascii());
|
||||
assert_eq!(bytes, write_ascii(&tags).into_bytes());
|
||||
assert!(contains(&bytes, b"A\\U+00E7\\U+00E3o fim"));
|
||||
fn unknown_ascii_and_utf8_code_pages_are_pure_ascii() {
|
||||
for cp in ["EBCDIC", "ASCII", "ascii ", "UTF8", "UNICODE"] {
|
||||
let tags = file(cp, "Ação\r\nfim");
|
||||
let bytes = write_ascii_in(&tags, cp);
|
||||
assert!(bytes.is_ascii(), "{cp}");
|
||||
assert_eq!(bytes, write_ascii(&tags).into_bytes(), "{cp}");
|
||||
assert!(contains(&bytes, b"A\\U+00E7\\U+00E3o fim"), "{cp}");
|
||||
assert!(write_binary_in(&tags, cp).iter().all(u8::is_ascii), "{cp} binary");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,25 @@ fn saving_writes_text_in_the_code_page() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A file whose header declares `ASCII` keeps that code page and stays ASCII when saved: its
|
||||
/// non-ASCII text is written as `\U+` escapes, not as Windows-1252 bytes.
|
||||
#[test]
|
||||
fn saving_an_ascii_file_keeps_it_ascii() {
|
||||
use cadcraft_io::{SaveOptions, Version};
|
||||
let d = cadcraft_io::read_dxf(&ascii_dxf("AC1015", "ASCII", b"El\\U+00E9trica", b"B", b"Eletrica \\U+00E9")).unwrap();
|
||||
assert_eq!(names(&d), ("Elétrica".into(), "B".into(), "Eletrica é".into()));
|
||||
for version in [Version::R2000, Version::R2004] {
|
||||
let ascii = cadcraft_io::write_with(&d, "x.dxf", &SaveOptions { version: Some(version), binary: false }).unwrap();
|
||||
assert!(ascii.is_ascii(), "{version}");
|
||||
let binary = cadcraft_io::write_with(&d, "x.dxf", &SaveOptions { version: Some(version), binary: true }).unwrap();
|
||||
for out in [&ascii, &binary] {
|
||||
assert!(out.windows(14).any(|w| w == b"El\\U+00E9trica"), "{version}");
|
||||
assert!(out.windows(5).any(|w| w == b"ASCII"), "{version}: the header keeps its code page");
|
||||
assert_eq!(names(&cadcraft_io::read_dxf(out).unwrap()), ("Elétrica".into(), "B".into(), "Eletrica é".into()), "{version}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A binary DXF: R2000 strings are in the code page, R2007+ strings UTF-8.
|
||||
#[test]
|
||||
fn binary_dxf_honours_the_code_page() {
|
||||
|
||||
Reference in New Issue
Block a user