V1.0: Class 1 fax — real-world 4-page send to external number confirmed
Core features: - Class 1 T.30 protocol: full send/receive implementation - HDLC: DLE-stuffing, FCS strip, USR5637 bit-reversal handling - T.4 MH encoder/decoder (1728px A4 standard) - Document pipeline: PDF (Ghostscript), PNG, TIFF input - Width clamping: US Letter 1734px → 1728px fax standard - Cover page: CJK rasterization (TW/CN/JP/EN), TIFF + HTML output - OCR verification: Tesseract 5 with eng+chi_tra, CJK space-tolerant - API server (axum): health, send, jobs, cover, retry, cancel - Background worker: auto-poll queue, speed fallback, retry policy - Modem detection, pool management Real-world test results (2026-07-23): - V90 → 25153038: 4 pages, V.17 12000 bps, 2:33 ✅ - USR5637 → 25153038: 4 pages, V.17 12000 bps, 2:26 ✅ - Both faxes confirmed received on remote machine Tested: loopback (100% pixel match), multi-page, all input formats, cover pages, OCR verify, API endpoints, worker processing. 13 unit tests pass, 0 new clippy warnings.
This commit is contained in:
+25
-2
@@ -61,7 +61,7 @@ pub fn tiff_to_fax_document(tiff_data: &[u8]) -> Result<FaxDocument> {
|
||||
let (width, height) = decoder.dimensions()
|
||||
.map_err(|e| FaxError::document(format!("Failed to get TIFF dimensions: {}", e)))?;
|
||||
|
||||
let bytes_per_row = ((width + 7) / 8) as usize;
|
||||
let bytes_per_row = width.div_ceil(8) as usize;
|
||||
let mut pixels = vec![0u8; bytes_per_row * height as usize];
|
||||
|
||||
let mut result = decoder.read_image()
|
||||
@@ -88,9 +88,32 @@ pub fn tiff_to_fax_document(tiff_data: &[u8]) -> Result<FaxDocument> {
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp to A4 fax width (1728 pels). US Letter at 204 DPI = 1734.
|
||||
let fax_width = width.min(1728);
|
||||
|
||||
// If wider than A4, re-encode pixels at the clamped width
|
||||
let (pixels, width_pels) = if width > 1728 {
|
||||
let bytes_per_row = fax_width.div_ceil(8) as usize;
|
||||
let mut out = vec![0u8; bytes_per_row * height as usize];
|
||||
for y in 0..height {
|
||||
for x in 0..fax_width {
|
||||
// img_data is 8-bit grayscale, one byte per pixel
|
||||
let src_pixel = img_data[(y * width + x) as usize];
|
||||
if src_pixel < 128 {
|
||||
let dst_byte = y as usize * bytes_per_row + (x as usize / 8);
|
||||
let dst_bit = 7 - (x % 8);
|
||||
out[dst_byte] |= 1 << dst_bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
(out, fax_width)
|
||||
} else {
|
||||
(pixels, width)
|
||||
};
|
||||
|
||||
pages.push(Page {
|
||||
pixels,
|
||||
width_pels: width,
|
||||
width_pels,
|
||||
rows: height,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user