[fix](trx-wefax): finalize and fsync PNG file before reporting path

Relying on Drop to write the PNG IEND trailer and flush BufWriter
silently swallows I/O errors, which can leave truncated/corrupted
image files on disk. Explicitly call writer.finish() to surface
encoding errors and sync_all() the File so bytes are durable before
WefaxEvent::Complete is emitted with the path (the frontend may read
the file immediately). The intermediate BufWriter is dropped since
we already buffer all rows and write them in a single call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 20:42:39 +02:00
parent d487711237
commit ede5e75dca
+18 -3
View File
@@ -4,7 +4,7 @@
//! Image buffer and PNG encoding for WEFAX decoded images.
use std::io::BufWriter;
use std::io::Write;
use std::path::{Path, PathBuf};
/// Image assembler: accumulates greyscale lines and encodes to PNG.
@@ -91,14 +91,16 @@ impl ImageAssembler {
let filename = generate_filename(freq_hz, mode);
let path = output_dir.join(&filename);
// We already buffer the image rows into `img_data` below and
// write them in a single call, so a BufWriter adds no value.
// Using the bare `File` also lets us fsync explicitly below.
let file = std::fs::File::create(&path)
.map_err(|e| format!("create PNG file '{}': {}", path.display(), e))?;
let w = BufWriter::new(file);
let width = self.pixels_per_line as u32;
let height = self.lines.len() as u32;
let mut encoder = png::Encoder::new(w, width, height);
let mut encoder = png::Encoder::new(&file, width, height);
encoder.set_color(png::ColorType::Grayscale);
encoder.set_depth(png::BitDepth::Eight);
@@ -116,6 +118,19 @@ impl ImageAssembler {
.write_image_data(&img_data)
.map_err(|e| format!("write PNG data: {}", e))?;
// Explicitly finish the writer (writes IEND). Relying on Drop
// alone swallows any I/O error and can yield a truncated file.
writer
.finish()
.map_err(|e| format!("finalize PNG: {}", e))?;
// Flush the underlying file so the data is durably on disk by
// the time we emit the WefaxEvent::Complete.
(&file)
.flush()
.map_err(|e| format!("flush PNG file: {}", e))?;
file.sync_all()
.map_err(|e| format!("sync PNG file: {}", e))?;
Ok(path)
}