fax-0.2.6/.cargo_vcs_info.json0000644000000001360000000000100116170ustar { "git": { "sha1": "40b226eb97b391355e971fd95a8dd532a4c127a0" }, "path_in_vcs": "" }fax-0.2.6/Cargo.lock0000644000000057440000000000100076040ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "fax" version = "0.2.6" dependencies = [ "fax_derive", "tiff", ] [[package]] name = "fax_derive" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "flate2" version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "jpeg-decoder" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "miniz_oxide" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tiff" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ "flate2", "jpeg-decoder", "weezl", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "weezl" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" fax-0.2.6/Cargo.toml0000644000000030540000000000100076170ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "fax" version = "0.2.6" authors = ["Sebastian K "] build = false exclude = [ "T-REC-T.6-198811-I!!PDF-E.pdf", ".vscode/*", ".gitignore", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Decoder and Encoder for CCITT Group 3 and 4 bi-level image encodings used by fax machines TIFF and PDF." readme = "README.md" keywords = [ "CCITT", "fax", "CCITTFaxDecode", ] license = "MIT" repository = "https://github.com/pdf-rs/fax" [features] debug = [] [lib] name = "fax" path = "src/lib.rs" [[example]] name = "bitdiff" path = "examples/bitdiff.rs" [[example]] name = "check" path = "examples/check.rs" [[example]] name = "fax2pbm" path = "examples/fax2pbm.rs" [[example]] name = "fax2tiff" path = "examples/fax2tiff.rs" [[example]] name = "pbm2fax" path = "examples/pbm2fax.rs" [[example]] name = "validate" path = "examples/validate.rs" [[test]] name = "check" path = "tests/check.rs" [dependencies.fax_derive] version = "0.2.0" [dev-dependencies.tiff] version = "0.9" fax-0.2.6/Cargo.toml.orig000064400000000000000000000010361046102023000132760ustar 00000000000000[package] name = "fax" version = "0.2.6" authors = ["Sebastian K "] edition = "2018" description = "Decoder and Encoder for CCITT Group 3 and 4 bi-level image encodings used by fax machines TIFF and PDF." license = "MIT" keywords = ["CCITT", "fax", "CCITTFaxDecode"] repository = "https://github.com/pdf-rs/fax" exclude = ["T-REC-T.6-198811-I!!PDF-E.pdf", ".vscode/*", ".gitignore"] [features] debug = [] [dependencies] fax_derive = { version = "0.2.0", path = "derive" } [dev-dependencies] tiff = { version = "0.9" } fax-0.2.6/README.md000064400000000000000000000003531046102023000116670ustar 00000000000000# CCITT Group 3 and 4 image encodings. Currently supported: - de- and encoding group 4 images - decoding group 3 images You can ask questions on [Zulip](https://type.zulipchat.com/#narrow/stream/209232-pdf/topic/fax.20.2F.20master) fax-0.2.6/examples/bitdiff.rs000064400000000000000000000010521046102023000142000ustar 00000000000000use std::fs; fn main() { let mut args = std::env::args().skip(1); let a = fs::read(&args.next().unwrap()).unwrap(); let b = fs::read(&args.next().unwrap()).unwrap(); for (i, (&a, &b)) in a.iter().zip(b.iter()).enumerate() { if a != b { println!("mismatch at byte {}: {:08b} vs. {:08b}", i, a, b); break; } } if a.len() > b.len() { println!("a has additional {:?}", &a[b.len()..]); } if b.len() > a.len() { println!("b has additional {:?}", &b[a.len()..]); } } fax-0.2.6/examples/check.rs000064400000000000000000000064251046102023000136570ustar 00000000000000#![feature(slice_split_once)] use fax::{VecWriter, decoder, decoder::pels, BitWriter, Bits, Color}; use std::io::Write; use std::fs::{self, File}; fn main() { let mut args = std::env::args().skip(1); let input: String = args.next().unwrap(); let reference = args.next().unwrap(); let ref_data = std::fs::read(&reference).unwrap(); let (header1, data) = ref_data.split_once(|&b| b == b'\n').unwrap(); assert_eq!(header1, b"P4"); let (header2, ref_image) = data.split_once(|&b| b == b'\n').unwrap(); let header2 = std::str::from_utf8(header2).unwrap(); dbg!(header2); let (w, h) = header2.split_once(" ").unwrap(); let width: u16 = w.parse().unwrap(); let h: u16 = h.parse().unwrap(); let mut ref_lines = ref_image.chunks_exact((width as usize + 7) / 8); let data; let inverted; if input.ends_with(".tiff") { use tiff::{decoder::Decoder, tags::Tag}; let tiff = std::fs::read(&input).unwrap(); let reader = std::io::Cursor::new(tiff.as_slice()); let mut decoder = Decoder::new(reader).unwrap(); let (w, h) = decoder.chunk_dimensions(); let mut buf = vec![0; w as usize * h as usize]; let strip_offset = decoder.get_tag(Tag::StripOffsets).unwrap().into_u32().unwrap() as usize; let strip_bytes = decoder.get_tag(Tag::StripByteCounts).unwrap().into_u32().unwrap() as usize; let interpr = decoder.get_tag(Tag::PhotometricInterpretation).unwrap().into_u16().unwrap(); data = tiff[strip_offset .. strip_offset + strip_bytes].to_vec(); inverted = interpr != 0; } else { data = fs::read(&input).unwrap(); inverted = false; } let mut height = 0; let (black, white) = match inverted { false => (Bits { data: 1, len: 1 }, Bits { data: 0, len: 1 }), true => (Bits { data: 0, len: 1 }, Bits { data: 1, len: 1 }) }; decoder::decode_g4(data.iter().cloned(), width, None, |transitions| { let mut writer = VecWriter::new(); for c in pels(transitions, width) { let bit = match c { Color::Black => black, Color::White => white, }; writer.write(bit); } writer.pad(); let data = writer.finish(); let ref_line = ref_lines.next().unwrap(); println!("{height:3} dec: {}", Line(&data)); if ref_line != data { println!(" ref: {}", Line(ref_line)); 'a: for (byte, (&r, &v)) in ref_line.iter().zip(data.iter()).enumerate() { if r != v { for i in (0 .. 8).rev() { if r & (1 << i) != v & (1 << i) { println!("mismatch at pos {}", (8 * byte) + 7-i); break 'a; } } } } panic!("decode error"); } height += 1; }); } struct Line<'a>(&'a [u8]); impl<'a> std::fmt::Display for Line<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let codes = [' ', '▐', '▌', '█']; for b in self.0.iter().flat_map(|b| [b >> 6, (b >> 4) & 3, (b >>2) & 3, b & 3]) { write!(f, "{}", codes[b as usize])?; } Ok(()) } } fax-0.2.6/examples/fax2pbm.rs000064400000000000000000000021071046102023000141320ustar 00000000000000use fax::{VecWriter, decoder, decoder::pels, BitWriter, Bits, Color}; use std::io::Write; use std::fs::{self, File}; fn main() { let mut args = std::env::args().skip(1); let input: String = args.next().unwrap(); let width: u16 = args.next().unwrap().parse().unwrap(); let output = args.next().unwrap(); let data = fs::read(&input).unwrap(); let mut writer = VecWriter::new(); let mut height = 0; decoder::decode_g4(data.iter().cloned(), width, None, |transitions| { for c in pels(transitions, width) { let bit = match c { Color::Black => Bits { data: 1, len: 1 }, Color::White => Bits { data: 0, len: 1 } }; writer.write(bit); } writer.pad(); height += 1; }); let data = writer.finish(); assert_eq!(data.len(), height as usize * ((width as usize + 7) / 8)); let header = format!("P4\n{} {}\n", width, height); let mut out = File::create(&output).unwrap(); out.write_all(header.as_bytes()).unwrap(); out.write_all(&data).unwrap(); }fax-0.2.6/examples/fax2tiff.rs000064400000000000000000000011111046102023000142760ustar 00000000000000use fax::tiff::wrap; use fax::{VecWriter, decoder, decoder::pels, BitWriter, Bits, Color}; use std::io::Write; use std::fs::{self, File}; fn main() { let mut args = std::env::args().skip(1); let input: String = args.next().unwrap(); let width: u16 = args.next().unwrap().parse().unwrap(); let output = args.next().unwrap(); let data = fs::read(&input).unwrap(); let mut height = 0; decoder::decode_g4(data.iter().cloned(), width, None, |transitions| { height += 1; }); std::fs::write(output, wrap(&data, width as _, height)).unwrap(); } fax-0.2.6/examples/pbm2fax.rs000064400000000000000000000021761046102023000141400ustar 00000000000000use fax::{encoder::Encoder, slice_bits, tiff, ByteReader, Color, VecWriter}; use std::fs; fn main() { let mut args = std::env::args().skip(1); let input: String = args.next().unwrap(); let output = args.next().unwrap(); let data = fs::read(&input).unwrap(); let mut parts = data.splitn(3, |&b| b == b'\n'); assert_eq!(parts.next().unwrap(), b"P4"); let mut size = parts.next().unwrap().splitn(2, |&b| b == b' '); let width: u32 = std::str::from_utf8(size.next().unwrap()).unwrap().parse().unwrap(); let height: u32 = std::str::from_utf8(size.next().unwrap()).unwrap().parse().unwrap(); let writer = VecWriter::new(); let mut encoder = Encoder::new(writer); for line in parts.next().unwrap().chunks((width as usize + 7) / 8).take(height as _) { let line = slice_bits(line).take(width as usize) .map(|b| match b { false => Color::White, true => Color::Black }); encoder.encode_line(line, width as u16).unwrap(); } let data = encoder.finish().unwrap().finish(); fs::write(&output, &tiff::wrap(&data, width, height)).unwrap(); } fax-0.2.6/examples/validate.rs000064400000000000000000000035541046102023000143730ustar 00000000000000use fax::{encoder::Encoder, slice_bits, slice_reader, BitReader, BitWriter, Bits, ByteReader, Color}; use std::{convert::Infallible, fs}; fn main() { let mut args = std::env::args().skip(1); let input: String = args.next().unwrap(); let output = args.next().unwrap(); let data = fs::read(&input).unwrap(); let reference_data = fs::read(&output).unwrap(); let mut parts = data.splitn(3, |&b| b == b'\n'); assert_eq!(parts.next().unwrap(), b"P4"); let mut size = parts.next().unwrap().splitn(2, |&b| b == b' '); let width: u16 = std::str::from_utf8(size.next().unwrap()).unwrap().parse().unwrap(); let height: u16 = std::str::from_utf8(size.next().unwrap()).unwrap().parse().unwrap(); //let writer = VecWriter::new(); let writer = Validator { reader: slice_reader(&reference_data) }; let mut encoder = Encoder::new(writer); for (y, line) in parts.next().unwrap().chunks((width as usize + 7) / 8).enumerate().take(height as _) { println!("\nline {}", y); let line = slice_bits(line).take(width as usize) .map(|b| match b { false => Color::Black, true => Color::White }); encoder.encode_line(line, width).unwrap(); } let mut writer = encoder.finish().unwrap(); writer.reader.print_remaining(); //let (data, _) = encoder.into_writer().finish(); //fs::write(&output, &data).unwrap(); } struct Validator { reader: ByteReader } impl BitWriter for Validator where ByteReader: BitReader { type Error = (); fn write(&mut self, bits: Bits) -> Result<(), ()> { let expected = Bits { data: self.reader.peek(bits.len).unwrap(), len: bits.len }; if expected != bits { println!("{} != {}", expected, bits); return Err(()); } self.reader.consume(bits.len); Ok(()) } }fax-0.2.6/src/decoder.rs000064400000000000000000000206631046102023000131600ustar 00000000000000use std::convert::Infallible; use std::io::{self, Bytes, Read}; use crate::{BitReader, ByteReader, Color, Transitions}; use crate::maps::{Mode, black, white, mode, EDFB_HALF, EOL}; fn with_markup(decoder: D, reader: &mut R) -> Option where D: Fn(&mut R) -> Option { let mut sum = 0; while let Some(n) = decoder(reader) { //print!("{} ", n); sum += n; if n < 64 { //debug!("= {}", sum); return Some(sum) } } None } fn colored(current: Color, reader: &mut impl BitReader) -> Option { //debug!("{:?}", current); match current { Color::Black => with_markup(black::decode, reader), Color::White => with_markup(white::decode, reader), } } /// Turn a list of color changing position into an iterator of pixel colors /// /// The width of the line/image has to be given in `width`. /// The iterator will produce exactly that many items. pub fn pels(line: &[u16], width: u16) -> impl Iterator + '_ { use std::iter::repeat; let mut color = Color::White; let mut last = 0; let pad_color = if line.len() & 1 == 1 { !color } else { color }; line.iter().flat_map(move |&p| { let c = color; color = !color; let n = p.saturating_sub(last); last = p; repeat(c).take(n as usize) }).chain(repeat(pad_color)).take(width as usize) } /// Decode a Group 3 encoded image. /// /// The callback `line_cb` is called for each decoded line. /// The argument is the list of positions of color change, starting with white. /// /// To obtain an iterator over the pixel colors, the `pels` function is provided. pub fn decode_g3(input: impl Iterator, mut line_cb: impl FnMut(&[u16])) -> Option<()> { let reader = input.map(Result::::Ok); let mut decoder = Group3Decoder::new(reader).ok()?; while let Ok(status) = decoder.advance() { if status == DecodeStatus::End { return Some(()); } line_cb(decoder.transitions()); } None } #[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum DecodeStatus { Incomplete, End, } pub struct Group3Decoder { reader: ByteReader, current: Vec } impl>> Group3Decoder { pub fn new(reader: R) -> Result> { let mut reader = ByteReader::new(reader).map_err(DecodeError::Reader)?; reader.expect(EOL).map_err(|_| DecodeError::Invalid)?; Ok(Group3Decoder { reader, current: vec![] }) } pub fn advance(&mut self) -> Result> { self.current.clear(); let mut a0 = 0; let mut color = Color::White; while let Some(p) = colored(color, &mut self.reader) { a0 += p; self.current.push(a0); color = !color; } self.reader.expect(EOL).map_err(|_| DecodeError::Invalid)?; for _ in 0 .. 6 { if self.reader.peek(EOL.len) == Some(EOL.data) { self.reader.consume(EOL.len).map_err(DecodeError::Reader)?; } else { return Ok(DecodeStatus::Incomplete) } } Ok(DecodeStatus::End) } pub fn transitions(&self) -> &[u16] { &self.current } } /// Decode a Group 4 Image /// /// - `width` is the width of the image. /// - The callback `line_cb` is called for each decoded line. /// The argument is the list of positions of color change, starting with white. /// /// If `height` is specified, at most that many lines will be decoded, /// otherwise data is decoded until the end-of-block marker (or end of data). /// /// To obtain an iterator over the pixel colors, the `pels` function is provided. pub fn decode_g4(input: impl Iterator, width: u16, height: Option, mut line_cb: impl FnMut(&[u16])) -> Option<()> { let reader = input.map(Result::::Ok); let mut decoder = Group4Decoder::new(reader, width).ok()?; for y in 0 .. height.unwrap_or(u16::MAX) { let status = decoder.advance().ok()?; if status == DecodeStatus::End { return Some(()); } line_cb(decoder.transition()); } Some(()) } #[derive(Debug)] pub enum DecodeError { Reader(E), Invalid, Unsupported, } impl std::fmt::Display for DecodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Decode Error") } } impl std::error::Error for DecodeError { } pub struct Group4Decoder { reader: ByteReader, reference: Vec, current: Vec, width: u16 } impl>> Group4Decoder { pub fn new(reader: R, width: u16) -> Result { Ok(Group4Decoder { reader: ByteReader::new(reader)?, reference: Vec::new(), current: Vec::new(), width }) } // when Complete::Complete is returned, there is no useful data in .transitions() or .line() pub fn advance(&mut self) -> Result> { let mut transitions = Transitions::new(&self.reference); let mut a0 = 0; let mut color = Color::White; let mut start_of_row = true; //debug!("\n\nline {}", y); loop { //reader.print_peek(); let mode = match mode::decode(&mut self.reader) { Some(mode) => mode, None => return Err(DecodeError::Invalid), }; //debug!(" {:?}, color={:?}, a0={}", mode, color, a0); match mode { Mode::Pass => { if start_of_row && color == Color::White { transitions.pos += 1; } else { transitions.next_color(a0, !color, false).ok_or(DecodeError::Invalid)?; } //debug!("b1={}", b1); if let Some(b2) = transitions.next() { //debug!("b2={}", b2); a0 = b2; } } Mode::Vertical(delta) => { let b1 = transitions.next_color(a0, !color, start_of_row).unwrap_or(self.width); let a1 = (b1 as i16 + delta as i16) as u16; if a1 >= self.width { break; } //debug!("transition to {:?} at {}", !color, a1); self.current.push(a1); color = !color; a0 = a1; if delta < 0 { transitions.seek_back(a0); } } Mode::Horizontal => { let a0a1 = colored(color, &mut self.reader).ok_or(DecodeError::Invalid)?; let a1a2 = colored(!color, &mut self.reader).ok_or(DecodeError::Invalid)?; let a1 = a0 + a0a1; let a2 = a1 + a1a2; //debug!("a0a1={}, a1a2={}, a1={}, a2={}", a0a1, a1a2, a1, a2); self.current.push(a1); if a2 >= self.width { break; } self.current.push(a2); a0 = a2; } Mode::Extension => { let xxx = self.reader.peek(3).ok_or(DecodeError::Invalid)?; // debug!("extension: {:03b}", xxx); self.reader.consume(3); // debug!("{:?}", current); return Err(DecodeError::Unsupported); } Mode::EOF => return Ok(DecodeStatus::End), } start_of_row = false; if a0 >= self.width { break; } } //debug!("{:?}", current); std::mem::swap(&mut self.reference, &mut self.current); self.current.clear(); Ok(DecodeStatus::Incomplete) } pub fn transition(&self) -> &[u16] { &self.reference } pub fn line(&self) -> Line { Line { transitions: &self.reference, width: self.width } } } pub struct Line<'a> { pub transitions: &'a [u16], pub width: u16 } impl<'a> Line<'a> { pub fn pels(&self) -> impl Iterator + 'a { pels(&self.transitions, self.width) } }fax-0.2.6/src/encoder.rs000064400000000000000000000103451046102023000131660ustar 00000000000000use crate::{Color, BitWriter, Transitions, maps::{Mode, mode, black, white, EDFB_HALF}}; fn absdiff(a: u16, b: u16) -> u16 { if a > b { a - b } else { b - a } } pub struct Encoder { writer: W, reference: Vec, current: Vec, } fn encode_color(writer: &mut W, color: Color, mut n: u16) -> Result<(), W::Error> { let table = match color { Color::White => &white::ENTRIES, Color::Black => &black::ENTRIES, }; let mut write = |n: u16| { let idx = if n >= 64 { 63 + n / 64 } else { n } as usize; let (v, bits) = table[idx]; assert_eq!(v, n); //debug!("{}", n); writer.write(bits) }; while n >= 2560 { write(2560)?; n -= 2560; } if n >= 64 { let d = n & !63; write(d)?; n -= d; } write(n) } impl Encoder { pub fn new(writer: W) -> Self { Encoder { writer, reference: vec![], current: vec![], } } pub fn encode_line(&mut self, pels: impl Iterator, width: u16) -> Result<(), W::Error> { let mut color = Color::White; let mut transitions = Transitions::new(&self.reference); let mut a0 = 0; let mut start_of_line = true; let mut pels = pels.enumerate() .scan(Color::White, |state, (i, c)| { Some(if c != *state { debug!(" {i} {c:?}"); *state = c; Some(i as u16) } else { None }) }).filter_map(|x| x); let writer = &mut self.writer; self.current.clear(); while a0 < width { let a1; if let Some(a1_) = pels.next() { self.current.push(a1_); a1 = a1_; } else { a1 = width; } loop { transitions.seek_back(a0); let b1 = transitions.next_color(a0, !color, start_of_line).unwrap_or(width); let b2 = transitions.peek(); start_of_line = false; debug!(" a0={a0}, a1={a1}, b1={:?}, b2={:?}", b1, b2); match (b1, b2) { (_b1, Some(b2)) if b2 < a1 => { debug!(" Pass"); let bits = mode::encode(Mode::Pass).unwrap(); writer.write(bits)?; transitions.skip(1); a0 = b2; continue; } (b1, _) if absdiff(a1, b1) <= 3 => { let delta = a1 as i16 - b1 as i16; debug!(" Vertical({})", delta); let bits = mode::encode(Mode::Vertical(delta as i8)).unwrap(); writer.write(bits)?; a0 = a1; color = !color; } _ => { let a2 = match pels.next() { Some(a2) => { self.current.push(a2); a2 }, None => width }; let a0a1 = a1 - a0; let a1a2 = a2 - a1; debug!(" Horizontal({}, {}) color={color:?}", a0a1, a1a2); let bits = mode::encode(Mode::Horizontal).unwrap(); writer.write(bits)?; let c = if a0 + a1 == 0 { Color::White } else { color }; encode_color(writer, c, a0a1)?; encode_color(writer, !c, a1a2)?; a0 = a2; } } break; } } std::mem::swap(&mut self.reference, &mut self.current); debug!("next line"); Ok(()) } pub fn finish(mut self) -> Result { self.writer.write(EDFB_HALF)?; self.writer.write(EDFB_HALF)?; Ok(self.writer) } }fax-0.2.6/src/lib.rs000064400000000000000000000167311046102023000123220ustar 00000000000000use std::convert::Infallible; use std::io::{self, Read}; use std::iter::Map; use std::ops::Not; use std::fmt; #[cfg(feature="debug")] macro_rules! debug { ($($arg:expr),*) => ( println!($($arg),*) ) } #[cfg(not(feature="debug"))] macro_rules! debug { ($($arg:expr),*) => ( () ) } pub mod maps; /// Decoder module pub mod decoder; /// Encoder module pub mod encoder; /// TIFF helper functions pub mod tiff; /// Trait used to read data bitwise. /// /// For lazy people `ByteReader` is provided which implements this trait. pub trait BitReader { type Error; /// look at the next (up to 16) bits of data /// /// Data is returned in the lower bits of the `u16`. fn peek(&self, bits: u8) -> Option; /// Consume the given amount of bits from the input. fn consume(&mut self, bits: u8) -> Result<(), Self::Error>; /// Assert that the next bits matches the given pattern. /// /// If it does not match, the found pattern is returned if enough bits are aviable. /// Otherwise None is returned. fn expect(&mut self, bits: Bits) -> Result<(), Option> { match self.peek(bits.len) { None => Err(None), Some(val) if val == bits.data => Ok(()), Some(val) => Err(Some(Bits { data: val, len: bits.len })) } } fn bits_to_byte_boundary(&self) -> u8; } /// Trait to write data bitwise /// /// The `VecWriter` struct is provided for convinience. pub trait BitWriter { type Error; fn write(&mut self, bits: Bits) -> Result<(), Self::Error>; } pub struct VecWriter { data: Vec, partial: u32, len: u8 } impl BitWriter for VecWriter { type Error = Infallible; fn write(&mut self, bits: Bits) -> Result<(), Self::Error> { self.partial |= (bits.data as u32) << (32 - self.len - bits.len); self.len += bits.len; while self.len >= 8 { self.data.push((self.partial >> 24) as u8); self.partial <<= 8; self.len -= 8; } Ok(()) } } impl VecWriter { pub fn new() -> Self { VecWriter { data: Vec::new(), partial: 0, len: 0 } } // with capacity of `n` bits. pub fn with_capacity(n: usize) -> Self { VecWriter { data: Vec::with_capacity((n + 7) / 8), partial: 0, len: 0 } } /// Pad the output with `0` bits until it is at a byte boundary. pub fn pad(&mut self) { if self.len > 0 { self.data.push((self.partial >> 24) as u8); self.partial = 0; self.len = 0; } } /// pad and return the accumulated bytes pub fn finish(mut self) -> Vec { self.pad(); self.data } } pub struct ByteReader { read: R, partial: u32, valid: u8, } impl>> ByteReader { /// Construct a new `ByteReader` from an iterator of `u8` pub fn new(read: R) -> Result { let mut bits = ByteReader { read, partial: 0, valid: 0 }; bits.fill()?; Ok(bits) } fn fill(&mut self) -> Result<(), E> { while self.valid < 16 { match self.read.next() { Some(Ok(byte)) => { self.partial = self.partial << 8 | byte as u32; self.valid += 8; } Some(Err(e)) => return Err(e), None => break } } Ok(()) } /// Print the remaining data /// /// Note: For debug purposes only, not part of the API. pub fn print_remaining(&mut self) { println!("partial: {:0w$b}, valid: {}", self.partial & ((1 << self.valid) - 1), self.valid, w=self.valid as usize); while let Some(Ok(b)) = self.read.next() { print!("{:08b} ", b); } println!(); } pub fn print_peek(&self) { println!("partial: {:0w$b}, valid: {}", self.partial & ((1 << self.valid) - 1), self.valid, w=self.valid as usize); } } pub fn slice_reader(slice: &[u8]) -> ByteReader> + '_> { ByteReader::new(slice.iter().cloned().map(Ok)).unwrap() } pub fn slice_bits(slice: &[u8]) -> impl Iterator + '_ { slice.iter().flat_map(|&b| [7,6,5,4,3,2,1,0].map(|i| (b >> i) & 1 != 0)) } impl>> BitReader for ByteReader { type Error = E; fn peek(&self, bits: u8) -> Option { assert!(bits <= 16); if self.valid >= bits { let shift = self.valid - bits; let out = (self.partial >> shift) as u16 & ((1 << bits) - 1); Some(out) } else { None } } fn consume(&mut self, bits: u8) -> Result<(), E> { self.valid -= bits; self.fill() } fn bits_to_byte_boundary(&self) -> u8 { self.valid & 7 } } #[test] fn test_bits() { let mut bits = slice_reader(&[0b0000_1101, 0b1010_0000]); assert_eq!(maps::black::decode(&mut bits), Some(42)); } /// Enum used to signal black/white. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Color { Black, White } impl Not for Color { type Output = Self; fn not(self) -> Self { match self { Color::Black => Color::White, Color::White => Color::Black, } } } struct Transitions<'a> { edges: &'a [u16], pos: usize } impl<'a> Transitions<'a> { fn new(edges: &'a [u16]) -> Self { Transitions { edges, pos: 0 } } fn seek_back(&mut self, start: u16) { self.pos = self.pos.min(self.edges.len().saturating_sub(1)); while self.pos > 0 { if start < self.edges[self.pos-1] { self.pos -= 1; } else { break; } } } fn next_color(&mut self, start: u16, color: Color, start_of_row: bool) -> Option { if start_of_row { if color == Color::Black { self.pos = 1; return self.edges.get(0).cloned() } else { self.pos = 2; return self.edges.get(1).cloned() } } while self.pos < self.edges.len() { if self.edges[self.pos] <= start { self.pos += 1; continue; } if (self.pos % 2 == 0) != (color == Color::Black) { self.pos += 1; } break; } if self.pos < self.edges.len() { let val = self.edges[self.pos]; self.pos += 1; Some(val) } else { None } } fn next(&mut self) -> Option { if self.pos < self.edges.len() { let val = self.edges[self.pos]; self.pos += 1; Some(val) } else { None } } fn peek(&self) -> Option { self.edges.get(self.pos).cloned() } fn skip(&mut self, n: usize) { self.pos += n; } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Bits { pub data: u16, pub len: u8 } impl fmt::Debug for Bits { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "d={:0b} w={}", self.data, self.len) } } impl fmt::Display for Bits { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:0w$b}", self.data & ((1 << self.len) - 1), w=self.len as usize) } }fax-0.2.6/src/maps.rs000064400000000000000000000161111046102023000125040ustar 00000000000000use fax_derive::bitmaps; use crate::{BitReader, Bits}; enum Entry { Leaf(u8, &'static [Option<(T, u8)>]), Value(T, u8), Prefix(u8, &'static [Entry]) } impl Entry { fn find(&self, reader: &mut impl BitReader) -> Option { match *self { Entry::Value(val, len) => { reader.consume(len); Some(val) } Entry::Leaf(width, lut) => { let index = reader.peek(width)?; let (val, len) = lut[index as usize]?; reader.consume(len); Some(val) } Entry::Prefix(width, lut) => { let index = reader.peek(width)?; let entry = &lut[index as usize]; match *entry { Entry::Value(val, len) => { reader.consume(len); Some(val) } _ => { reader.consume(width); entry.find(reader) } } } } } } pub const EDFB_HALF: Bits = Bits { data: 1, len: 12 }; pub const EOL: Bits = Bits { data: 1, len: 12 }; #[derive(Copy, Clone, Debug)] pub enum Mode { Pass, Horizontal, Vertical(i8), Extension, EOF, } bitmaps! { mode { 0001 => Mode::Pass, 001 => Mode::Horizontal, 1 => Mode::Vertical(0), 011 => Mode::Vertical(1), 000011 => Mode::Vertical(2), 0000011 => Mode::Vertical(3), 010 => Mode::Vertical(-1), 000010 => Mode::Vertical(-2), 0000010 => Mode::Vertical(-3), 0000001 => Mode::Extension, 000000000001 => Mode::EOF, }, black { 0000110111 => 0, 010 => 1, 11 => 2, 10 => 3, 011 => 4, 0011 => 5, 0010 => 6, 00011 => 7, 000101 => 8, 000100 => 9, 0000100 => 10, 0000101 => 11, 0000111 => 12, 00000100 => 13, 00000111 => 14, 000011000 => 15, 0000010111 => 16, 0000011000 => 17, 0000001000 => 18, 00001100111 => 19, 00001101000 => 20, 00001101100 => 21, 00000110111 => 22, 00000101000 => 23, 00000010111 => 24, 00000011000 => 25, 000011001010 => 26, 000011001011 => 27, 000011001100 => 28, 000011001101 => 29, 000001101000 => 30, 000001101001 => 31, 000001101010 => 32, 000001101011 => 33, 000011010010 => 34, 000011010011 => 35, 000011010100 => 36, 000011010101 => 37, 000011010110 => 38, 000011010111 => 39, 000001101100 => 40, 000001101101 => 41, 000011011010 => 42, 000011011011 => 43, 000001010100 => 44, 000001010101 => 45, 000001010110 => 46, 000001010111 => 47, 000001100100 => 48, 000001100101 => 49, 000001010010 => 50, 000001010011 => 51, 000000100100 => 52, 000000110111 => 53, 000000111000 => 54, 000000100111 => 55, 000000101000 => 56, 000001011000 => 57, 000001011001 => 58, 000000101011 => 59, 000000101100 => 60, 000001011010 => 61, 000001100110 => 62, 000001100111 => 63, 0000001111 => 64, 000011001000 => 128, 000011001001 => 192, 000001011011 => 256, 000000110011 => 320, 000000110100 => 384, 000000110101 => 448, 0000001101100 => 512, 0000001101101 => 576, 0000001001010 => 640, 0000001001011 => 704, 0000001001100 => 768, 0000001001101 => 832, 0000001110010 => 896, 0000001110011 => 960, 0000001110100 => 1024, 0000001110101 => 1088, 0000001110110 => 1152, 0000001110111 => 1216, 0000001010010 => 1280, 0000001010011 => 1344, 0000001010100 => 1408, 0000001010101 => 1472, 0000001011010 => 1536, 0000001011011 => 1600, 0000001100100 => 1664, 0000001100101 => 1728, 00000001000 => 1792, 00000001100 => 1856, 00000001101 => 1920, 000000010010 => 1984, 000000010011 => 2048, 000000010100 => 2112, 000000010101 => 2176, 000000010110 => 2240, 000000010111 => 2304, 000000011100 => 2368, 000000011101 => 2432, 000000011110 => 2496, 000000011111 => 2560, }, white { 00110101 => 0, 000111 => 1, 0111 => 2, 1000 => 3, 1011 => 4, 1100 => 5, 1110 => 6, 1111 => 7, 10011 => 8, 10100 => 9, 00111 => 10, 01000 => 11, 001000 => 12, 000011 => 13, 110100 => 14, 110101 => 15, 101010 => 16, 101011 => 17, 0100111 => 18, 0001100 => 19, 0001000 => 20, 0010111 => 21, 0000011 => 22, 0000100 => 23, 0101000 => 24, 0101011 => 25, 0010011 => 26, 0100100 => 27, 0011000 => 28, 00000010 => 29, 00000011 => 30, 00011010 => 31, 00011011 => 32, 00010010 => 33, 00010011 => 34, 00010100 => 35, 00010101 => 36, 00010110 => 37, 00010111 => 38, 00101000 => 39, 00101001 => 40, 00101010 => 41, 00101011 => 42, 00101100 => 43, 00101101 => 44, 00000100 => 45, 00000101 => 46, 00001010 => 47, 00001011 => 48, 01010010 => 49, 01010011 => 50, 01010100 => 51, 01010101 => 52, 00100100 => 53, 00100101 => 54, 01011000 => 55, 01011001 => 56, 01011010 => 57, 01011011 => 58, 01001010 => 59, 01001011 => 60, 00110010 => 61, 00110011 => 62, 00110100 => 63, 11011 => 64, 10010 => 128, 010111 => 192, 0110111 => 256, 00110110 => 320, 00110111 => 384, 01100100 => 448, 01100101 => 512, 01101000 => 576, 01100111 => 640, 011001100 => 704, 011001101 => 768, 011010010 => 832, 011010011 => 896, 011010100 => 960, 011010101 => 1024, 011010110 => 1088, 011010111 => 1152, 011011000 => 1216, 011011001 => 1280, 011011010 => 1344, 011011011 => 1408, 010011000 => 1472, 010011001 => 1536, 010011010 => 1600, 011000 => 1664, 010011011 => 1728, 00000001000 => 1792, 00000001100 => 1856, 00000001101 => 1920, 000000010010 => 1984, 000000010011 => 2048, 000000010100 => 2112, 000000010101 => 2176, 000000010110 => 2240, 000000010111 => 2304, 000000011100 => 2368, 000000011101 => 2432, 000000011110 => 2496, 000000011111 => 2560, }, } fax-0.2.6/src/tiff.rs000064400000000000000000000044331046102023000125000ustar 00000000000000#[derive(Copy, Clone)] enum Value { Short(u16), Long(u32), Rational(u32, u32), DataOffset, } pub fn wrap(data: &[u8], width: u32, height: u32) -> Vec { use Value::*; let header_data = [ (256, Long(width)), // ImageWidth (257, Long(height)), // ImageLength (259, Short(4)), // Compression (262, Short(0)), // PhotometricInterpretation (273, DataOffset), // StripOffsets (274, Short(1)), // Orientation (278, Long(height)), // RowsPerStrip (279, Long(data.len() as u32)), // StripByteCounts (282, Rational(200, 1)), // XResolution (283, Rational(200, 1)), // YResolution (296, Short(2)), // ResolutionUnit ]; let rat_data_len = 2 * 8; // number of rationals * 8 let ifd_end = 4 + // magic 4 + // IFD offset 2 + // IFD entry count 12 * header_data.len() + // IFD enties 4; // null pointer at end of IFD let header_size = ifd_end + rat_data_len; let mut out = Vec::with_capacity(header_size + data.len()); out.extend_from_slice(&[73, 73, 42, 0]); let ifd_offset: u32 = 8; out.extend_from_slice(&ifd_offset.to_le_bytes()); out.extend_from_slice(&u16::to_le_bytes(header_data.len() as u16)); let mut num_rat = 0; for &(tag, val) in header_data.iter() { let (typ_num, val) = match val { Short(n) => (3, n as u32), Long(n) => (4, n), Rational(_, _) => { let o = ifd_end + 8 * num_rat; num_rat += 1; (5, o as u32) } DataOffset => (4, header_size as u32) }; let count = 1; out.extend_from_slice(&u16::to_le_bytes(tag)); out.extend_from_slice(&u16::to_le_bytes(typ_num)); out.extend_from_slice(&u32::to_le_bytes(count)); out.extend_from_slice(&u32::to_le_bytes(val)); } // NULL at IFD end out.extend_from_slice(&[0; 4]); // write additional data for &(_, val) in header_data.iter() { if let Value::Rational(nom, denom) = val { out.extend_from_slice(&nom.to_le_bytes()); out.extend_from_slice(&denom.to_le_bytes()); } } assert_eq!(out.len(), header_size); out.extend_from_slice(data); out } fax-0.2.6/tests/check.rs000064400000000000000000000130751046102023000132020ustar 00000000000000#![feature(slice_split_once)] use fax::{encoder, slice_bits, slice_reader, BitReader, ByteReader}; use fax::{VecWriter, decoder, decoder::pels, BitWriter, Bits, Color}; use std::fmt::Debug; use std::io::Write; use std::fs::{self, File}; use std::path::Path; #[test] fn main() { let data_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../fax-test"); let mut fails = vec![]; for r in data_path.read_dir().unwrap() { let e = r.unwrap(); let p = e.path(); let base = data_path.join(p.file_stem().unwrap()); let pbm = base.with_extension("pbm"); let r = if p.extension().is_some_and(|e| e == "fax") { read_pbm(&pbm).test_fax(&p) } else if p.extension().is_some_and(|e| e == "tiff") { read_pbm(&pbm).test_tiff(&p) } else { continue; }; println!("{base:?} {r:?}"); if r.is_err() { fails.push(p); } } if fails.len() > 0 { println!("failures: {fails:?}"); //panic!(""); } } struct TestImage { width: u16, height: u16, data: Vec, } fn read_pbm(path: &Path) -> TestImage { let ref_data = std::fs::read(path).unwrap(); let (header1, data) = ref_data.split_once(|&b| b == b'\n').unwrap(); assert_eq!(header1, b"P4"); let (header2, ref_image) = data.split_once(|&b| b == b'\n').unwrap(); let header2 = std::str::from_utf8(header2).unwrap(); let (w, h) = header2.split_once(" ").unwrap(); let width: u16 = w.parse().unwrap(); let h: u16 = h.parse().unwrap(); TestImage { width, height: h, data: ref_image.to_vec() } } impl TestImage { fn test_fax(&self, fax_path: &Path) -> Result<(), ()> { let data = fs::read(fax_path).unwrap(); self.test_stream(&data, false) } fn test_tiff(&self, path: &Path) -> Result<(), ()> { use tiff::{decoder::Decoder, tags::Tag}; let data = std::fs::read(path).unwrap(); let reader = std::io::Cursor::new(data.as_slice()); let mut decoder = Decoder::new(reader).unwrap(); let strip_offset = decoder.get_tag(Tag::StripOffsets).unwrap().into_u32().unwrap() as usize; let strip_bytes = decoder.get_tag(Tag::StripByteCounts).unwrap().into_u32().unwrap() as usize; decoder.goto_offset_u64(strip_offset as _).unwrap(); let white_is_1 = decoder.get_tag(Tag::PhotometricInterpretation).unwrap().into_u16().unwrap() != 0; let data = &data[strip_offset .. strip_offset + strip_bytes]; self.test_stream(&data, white_is_1) } fn test_stream(&self, data: &[u8], white_is_1: bool) -> Result<(), ()> { let mut ref_lines = self.data.chunks_exact((self.width as usize + 7) / 8).take(self.height as _); let (black, white) = match white_is_1 { false => (Bits { data: 1, len: 1 }, Bits { data: 0, len: 1 }), true => (Bits { data: 0, len: 1 }, Bits { data: 1, len: 1 }) }; let mut height = 0; let mut errors = 0; let ok = decoder::decode_g4(data.iter().cloned(), self.width, None, |transitions| { //println!("{}", transitions.len()); let mut writer = VecWriter::new(); for c in pels(transitions, self.width) { let bit = match c { Color::Black => black, Color::White => white }; writer.write(bit).unwrap(); } writer.pad(); let data = writer.finish(); let ref_line = ref_lines.next().unwrap(); if ref_line != data { println!("line {height} mismatch"); errors += 1; } height += 1; }).is_some(); if errors > 0 { println!("{} errors", errors); if height == self.height { return Ok(()); } return Err(()); } if !ok { println!("not ok"); return Err(()); } fn pixels(line: &[u8], white_is_1: bool) -> impl Iterator + '_ { slice_bits(line).map(move |b| if b ^ white_is_1 { Color::Black } else { Color::White }) } let mut expected = slice_reader(data); let mut encoder = encoder::Encoder::new(TestWriter { expected: &mut expected, offset: 0 }); let ref_lines = self.data.chunks_exact((self.width as usize + 7) / 8).take(self.height as _); let mut fail = false; for (i, line) in ref_lines.enumerate() { println!("line {i}"); if encoder.encode_line(pixels(line, white_is_1), self.width).is_err() { println!("fail at line {i} of {}", self.height); fail = true; break; } } dbg!(fax::maps::mode::decode(&mut expected)); if fail { return Err(()); } Ok(()) } } struct TestWriter<'a, R> { offset: usize, expected: &'a mut ByteReader, } impl<'a, E: Debug, R: Iterator>> BitWriter for TestWriter<'a, R> { type Error = (usize, u8); fn write(&mut self, bits: Bits) -> Result<(), Self::Error> { match self.expected.expect(bits) { Ok(()) => { self.expected.consume(bits.len).unwrap(); } Err(_) => { self.expected.print_peek(); println!(" @{}+{} found {}", self.offset/8, self.offset%8, bits); return Err((self.offset / 8, (self.offset % 8) as u8)); }, } self.offset += bits.len as usize; Ok(()) } }