Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libfaxtiff

A small, dependency-free C library for CCITT Group 3 / Group 4 fax compression — the bitonal codec used by fax machines, TIFF, and PDF.

The encode/decode core is the CCITT fax implementation carved out of libtiff (Sam Leffler / Silicon Graphics), lifted out of the TIFF container machinery and wrapped behind a tiny buffer-in / buffer-out API. It compresses and decompresses raw 1-bit-per-pixel raster data; it does not read or write TIFF files.

Supported variants

Variant Standard Description
FAXTIFF_VARIANT_G3_1D ITU-T T.4 1D Group 3, one-dimensional
FAXTIFF_VARIANT_G3_2D ITU-T T.4 2D Group 3, two-dimensional (K factor)
FAXTIFF_VARIANT_G4 ITU-T T.6 Group 4, MMR (best ratio)

Building

meson setup build
meson compile -C build

This builds the library plus two command-line tools in utils/:

  • cfc — encode a monochrome BMP into a self-describing .cfax stream.
  • cfd — decode a .cfax stream back to PBM (P4) or raw 1bpp.

They double as worked examples; the snippets below are distilled from their inner loops.

Pixel data format

Both directions speak the same raster layout — there is no TIFF, no palette, no header:

  • 1 bit per pixel, packed MSB-first within each byte.
  • A set bit (1) is black; a clear bit (0) is white.
  • Each row is ceil(columns / 8) bytes; the final byte of a row is padded to a byte boundary, and rows follow one another with no padding between them.

So a full raster is rows * ceil(columns / 8) bytes.

API

The public surface is one header — <libfaxtiff/libfaxtiff.h> — and three functions, all prefixed faxtiff_*.

Parameters

struct faxtiff_params_t {
    faxtiff_variant_t  variant;       /* G3_1D | G3_2D | G4         (required) */
    int                 columns;       /* image width in pixels      (required) */
    int                 rows;          /* image height in rows       (required) */
    faxtiff_fill_t     fill;          /* MSB2LSB (default) | LSB2MSB           */
    int                 maxk;          /* G3_2D K factor: 2 or 4; else ignored  */
    int                 end_of_line;   /* emit/expect EOL per row (G3)          */
    int                 end_of_block;  /* emit/expect RTC (G3) / EOFB (G4)      */
};

fill selects the bit order of the compressed stream (FAXTIFF_FILL_MSB2LSB is the usual default). maxk is consulted only for FAXTIFF_VARIANT_G3_2D (use 2 for ≤200 lpi, 4 for higher); pass 2 otherwise. The same parameters must be supplied to decode that were used to encode — the compressed stream does not carry its own geometry, so you must transmit it out of band (that is exactly what the .cfax container header in utils/ does).

Result buffer

struct faxtiff_buffer_t {
    char    *buf;   /* heap-allocated data                       */
    size_t   cap;   /* allocated capacity of buf                 */
    size_t   len;   /* meaningful bytes written into buf         */
};

Both calls return a freshly allocated faxtiff_buffer_t *, or NULL on failure. The caller owns it and frees it in two steps:

free(out->buf);
free(out);

Functions

const char *faxtiff_version(void);

struct faxtiff_buffer_t *
faxtiff_encode(const char *src, const struct faxtiff_params_t *params);

struct faxtiff_buffer_t *
faxtiff_decode(const char *src, size_t srclen,
                const struct faxtiff_params_t *params);

For faxtiff_encode, out->len is the compressed byte count. For faxtiff_decode, out->len is rows * ceil(columns / 8) of unpacked raster.

Example: encoding

Given width × height pixels already in the 1bpp MSB-first layout described above (here as const char *pixels):

#include <libfaxtiff/libfaxtiff.h>

struct faxtiff_params_t p = {
    .variant      = FAXTIFF_VARIANT_G4,     /* MMR — best compression */
    .columns      = width,
    .rows         = height,
    .fill         = FAXTIFF_FILL_MSB2LSB,
    .maxk         = 2,                       /* only consulted for G3_2D */
    .end_of_line  = 0,
    .end_of_block = 0,
};

struct faxtiff_buffer_t *enc = faxtiff_encode(pixels, &p);
if (!enc) {
    /* bad parameters or out of memory */
    return -1;
}

/* enc->len bytes of compressed fax data live in enc->buf. */
fwrite(enc->buf, 1, enc->len, stdout);

free(enc->buf);
free(enc);

Example: decoding

Given a compressed stream in enc / enclen, plus the geometry it was encoded with (carried alongside the data — see the .cfax header cfd reads):

#include <libfaxtiff/libfaxtiff.h>

struct faxtiff_params_t p = {
    .variant      = FAXTIFF_VARIANT_G4,
    .columns      = width,                   /* must match the encoder */
    .rows         = height,
    .fill         = FAXTIFF_FILL_MSB2LSB,
    .maxk         = 2,
    .end_of_line  = 0,
    .end_of_block = 0,
};

struct faxtiff_buffer_t *raw = faxtiff_decode(enc, enclen, &p);
if (!raw) {
    /* corrupt stream, geometry mismatch, or out of memory */
    return -1;
}

/* raw->len == rows * ceil(columns / 8), same 1bpp MSB-first packing. */
fwrite(raw->buf, 1, raw->len, stdout);

free(raw->buf);
free(raw);

License

The CCITT codec sources (fax3.c, fax3.h, fax3sm.c, t4.h) retain their original libtiff copyright and permission notices (Sam Leffler / Silicon Graphics). libfaxtiff is distributed under the same libtiff license; see LICENSE.md.

About

CCITT fax decoder carved from libtiff

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages