This commit is contained in:
2025-07-16 14:41:19 +03:00
parent 2a1ddbedf2
commit ec39173514
51 changed files with 11380 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
# GNU makefile for the JBIG-KIT PBM tools
.DELETE_ON_ERROR:
# Select an ANSI/ISO C compiler here, e.g. GNU gcc is recommended
CC = gcc
# Options for the compiler
CFLAGS = -g -O -W -Wall -Wno-unused-result -ansi -pedantic # --coverage
CPPFLAGS = -I../libjbig
LDFLAGS = -L../libjbig
.PHONY: txt pdf test test82 test85 clean
all: pbmtojbg jbgtopbm pbmtojbg85 jbgtopbm85 txt
txt: pbmtojbg.txt jbgtopbm.txt pbm.txt pgm.txt
pdf: pbmtojbg.pdf jbgtopbm.pdf pbm.pdf pgm.pdf
pbmtojbg: pbmtojbg.o ../libjbig/libjbig.a
$(CC) $(LDFLAGS) $(CFLAGS) -o pbmtojbg pbmtojbg.o -ljbig
jbgtopbm: jbgtopbm.o ../libjbig/libjbig.a
$(CC) $(LDFLAGS) $(CFLAGS) -o jbgtopbm jbgtopbm.o -ljbig
pbmtojbg85: pbmtojbg85.o ../libjbig/libjbig85.a
$(CC) $(LDFLAGS) $(CFLAGS) -o pbmtojbg85 pbmtojbg85.o -ljbig85
jbgtopbm85: jbgtopbm85.o ../libjbig/libjbig85.a
$(CC) $(LDFLAGS) $(CFLAGS) -o jbgtopbm85 jbgtopbm85.o -ljbig85
jbgtopbm.o: jbgtopbm.c ../libjbig/jbig.h
pbmtojbg.o: pbmtojbg.c ../libjbig/jbig.h
jbgtopbm85.o: jbgtopbm85.c ../libjbig/jbig85.h
pbmtojbg85.o: pbmtojbg85.c ../libjbig/jbig85.h
../libjbig/libjbig.a: ../libjbig/jbig.c ../libjbig/jbig.h \
../libjbig/jbig_ar.c ../libjbig/jbig_ar.h
$(MAKE) -C ../libjbig libjbig.a
../libjbig/libjbig85.a: ../libjbig/jbig85.c ../libjbig/jbig85.h \
../libjbig/jbig_ar.c ../libjbig/jbig_ar.h
$(MAKE) -C ../libjbig libjbig85.a
analyze:
clang $(CPPFLAGS) --analyze *.c
test: test82 test85
test82: pbmtojbg jbgtopbm
$(MAKE) IMG=ccitt1 OPTIONSP= dotest1
$(MAKE) IMG=ccitt2 OPTIONSP= dotest1
$(MAKE) IMG=ccitt3 OPTIONSP= dotest1
$(MAKE) IMG=xvlogo "OPTIONSP=-d 3" dotest1
$(MAKE) IMG=sandra OPTIONSP= OPTIONSJ= dotest2g
$(MAKE) IMG=sandra OPTIONSP=-b OPTIONSJ=-b dotest2g
$(MAKE) IMG=sandra OPTIONSP=-q OPTIONSJ= dotest2g
$(MAKE) IMG=sandra "OPTIONSP=-o 0" OPTIONSJ= dotest2g
$(MAKE) IMG=sandra "OPTIONSP=-o 2" OPTIONSJ= dotest2g
$(MAKE) IMG=multi OPTIONSP= OPTIONSJ= dotest2g
$(MAKE) IMG=multi OPTIONSP=-b OPTIONSJ=-b dotest2g
$(MAKE) IMG=mx "OPTIONSP=-q -s 3 -m 127" dotest1
$(MAKE) IMG=mx "OPTIONSP=-q -s 3 -m 127" dotest2b
$(MAKE) IMG=mx "OPTIONSP=-q -s 3 -m 127 -p 92" dotest2b
$(MAKE) IMG=mx "OPTIONSP=-q -Y -1" dotest2b
$(MAKE) IMG=mx "OPTIONSP=-Y -1" dotest2b
rm -f test-*.jbg test-*.pbm test-*.pgm
./jbgtopbm ../examples/ccitt1.jbg | ./pbmtojbg > test-ccitt1.jbg
cmp ../examples/ccitt1.jbg test-ccitt1.jbg
rm -f test-*.jbg test-*.pbm test-*.pgm
./jbgtopbm < ../examples/ccitt1.jbg | ./pbmtojbg - test-ccitt1.jbg
cmp ../examples/ccitt1.jbg test-ccitt1.jbg
rm -f test-*.jbg test-*.pbm test-*.pgm
./jbgtopbm < ../examples/ccitt1.jbg - test-ccitt1.pbm ; \
./pbmtojbg test-ccitt1.pbm test-ccitt1.jbg
cmp ../examples/ccitt1.jbg test-ccitt1.jbg
rm -f test-*.jbg test-*.pbm test-*.pgm
./jbgtopbm ../examples/ccitt1.jbg test-ccitt1.pbm ; \
./pbmtojbg test-ccitt1.pbm >test-ccitt1.jbg
cmp ../examples/ccitt1.jbg test-ccitt1.jbg
rm -f test-*.jbg test-*.pbm test-*.pgm
@echo
@echo "The pbmtools have PASSED the functional tests. Good!"
@echo
dotest1:
./jbgtopbm ../examples/$(IMG).jbg test-$(IMG).pbm
./pbmtojbg $(OPTIONSP) test-$(IMG).pbm test-$(IMG).jbg
cmp test-$(IMG).jbg ../examples/$(IMG).jbg
dotest2b:
./pbmtojbg $(OPTIONSP) test-$(IMG).pbm test-$(IMG).jbg
./jbgtopbm $(OPTIONSJ) test-$(IMG).jbg test-$(IMG)-2.pbm
cmp test-$(IMG).pbm test-$(IMG)-2.pbm
dotest2g:
./pbmtojbg $(OPTIONSP) ../examples/$(IMG).pgm test-$(IMG).jbg
./jbgtopbm $(OPTIONSJ) test-$(IMG).jbg test-$(IMG).pgm
cmp test-$(IMG).pgm ../examples/$(IMG).pgm
test85: pbmtojbg jbgtopbm pbmtojbg85 jbgtopbm85 test-t82.pbm
$(MAKE) IMG=t82 "OPTIONSP=-p 0" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-p 8" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-p 8 -r" dotest85b
$(MAKE) IMG=t82 "OPTIONSP=-p 64" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-p 72" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-s 2 -C c" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-s 99999" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y 9999 0" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y 1951 0" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y -1 127" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y -1 128" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y -1 1919" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y -1 1920" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y -1 1949" dotest85
$(MAKE) IMG=t82 "OPTIONSP=-Y -1 1950" dotest85
$(MAKE) IMG=ccitt1 dotest85
$(MAKE) IMG=ccitt2 dotest85
$(MAKE) IMG=ccitt3 dotest85
rm -f test-*.jbg test-*.jbg85 test-*.pbm
@echo
@echo "The T.85 pbmtools have PASSED the functional tests. Good!"
@echo
dotest85: test-$(IMG).pbm
./pbmtojbg85 $(OPTIONSP) test-$(IMG).pbm test-$(IMG).jbg85
ls -l test-$(IMG).jbg85
./jbgtopbm test-$(IMG).jbg85 test-$(IMG).pbm85
cmp test-$(IMG).pbm test-$(IMG).pbm85
rm test-$(IMG).pbm85
./jbgtopbm85 test-$(IMG).jbg85 test-$(IMG).pbm85
cmp test-$(IMG).pbm test-$(IMG).pbm85
rm test-$(IMG).pbm85
./jbgtopbm85 -B 1 test-$(IMG).jbg85 test-$(IMG).pbm85
cmp test-$(IMG).pbm test-$(IMG).pbm85
dotest85b: test-$(IMG).pbm
./pbmtojbg -f $(OPTIONSP) test-$(IMG).pbm test-$(IMG).jbg85
ls -l test-$(IMG).jbg85
./jbgtopbm test-$(IMG).jbg85 test-$(IMG).pbm85
cmp test-$(IMG).pbm test-$(IMG).pbm85
rm test-$(IMG).pbm85
./jbgtopbm85 test-$(IMG).jbg85 test-$(IMG).pbm85
cmp test-$(IMG).pbm test-$(IMG).pbm85
rm test-$(IMG).pbm85
./jbgtopbm85 -B 1 test-$(IMG).jbg85 test-$(IMG).pbm85
cmp test-$(IMG).pbm test-$(IMG).pbm85
test-%.pbm: ../examples/%.jbg
./jbgtopbm $< $@
test-t82.pbm:
$(MAKE) -C ../libjbig tstcodec
../libjbig/tstcodec $@
FOPT=-c 1000 -p 300000 -m 3
fuzz: test-t82.pbm
while \
./pbmtojbg -f test-t82.pbm | ./jbgfuzz.pl $(FOPT) && \
./pbmtojbg test-t82.pbm | ./jbgfuzz.pl $(FOPT) -d jbgtopbm ; \
do true; done
MAN2TXT=groff -man -Tascii -P -c -P -b -P -u
%.txt: %.1
$(MAN2TXT) $< >$@
%.txt: %.5
$(MAN2TXT) $< >$@
MAN2PS=groff -man -Tps
%.ps: %.1
$(MAN2PS) $< >$@
%.ps: %.5
$(MAN2PS) $< >$@
%.pdf: %.ps
ps2pdf $<
clean:
rm -f *.o *~ core pbmtojbg jbgtopbm pbmtojbg85 jbgtopbm85
rm -f test-*.jbg test-*.pbm test-*.pgm test-*.jbg85 test-*.pbm85
rm -f *.gcda *.gcno *.plist
rm -f *.ps *.pdf

View File

@@ -0,0 +1,105 @@
#!/usr/bin/perl
# Simple fuzz tester for JBIG-KIT decoder -- Markus Kuhn
#
# Usage example:
#
# $ ../libjbig/tstcodec t.pbm
# $ ./pbmtojbg -f t.pbm | ./jbgfuzz.pl
use strict;
my $fntst = '/tmp/test.jbg'; # fuzz testing file to be generated
my $fntmp = $fntst . '~'; # temporary file (for atomic update)
my $fnvalid = '-'; # valid example BIE file
my $pbmtools = '.'; # location of jbgtopbm and jbgtopbm85
my $count = "inf"; # how many times shall we try?
my @decoders;
my $prefix_len = 2000;
my $rnd_suffix_len = 2000;
my $mutation_rate = 10; # percentage of bytes substituted in prefix
while ($_ = shift @ARGV) {
if ($_ eq '-c') {
$count = shift @ARGV;
} elsif ($_ eq '-m') {
$mutation_rate = shift @ARGV;
} elsif ($_ eq '-p') {
$prefix_len = shift @ARGV;
} elsif ($_ eq '-r') {
$rnd_suffix_len = shift @ARGV;
} elsif ($_ eq '-d') {
push @decoders, shift @ARGV;
} elsif ($_ eq '-t') {
$pbmtools = shift @ARGV;
} else {
$fnvalid = $_;
}
}
@decoders = ('jbgtopbm', 'jbgtopbm85') unless @decoders;
# read some bytes from a valid BIE
my $valid_prefix;
my $in;
open($in, "<$fnvalid") || die("$fnvalid: $!\n");
read $in, $valid_prefix, $prefix_len;
close $in || die("$fnvalid: $!\n");
# open a source of random bytes
my $fn_rnd = '/dev/urandom';
my $rnd;
open($rnd, '<', $fn_rnd) || die;
for (my $i = 0; $i < $count; $i++) {
my $out;
open($out, '>', $fntmp) || die("$fntmp: $!\n");
my $prefix;
# randomly substitute some prefix bytes with random bytes
$prefix = $valid_prefix;
if (length($prefix) != $prefix_len) {
warn("Truncating requested $prefix_len byte prefix to available ".
length($prefix)." bytes.\n");
$prefix_len = length($prefix);
}
#print "\nB: ".join(',', unpack('C4N3C4', substr($prefix, 0, 20)))."\n";
for (my $p = 0; $p < $prefix_len; $p++) {
if (rand(100) < $mutation_rate) {
substr($prefix, $p, 1) = chr(int(rand(256)));
}
}
#print "A: ".join(',', unpack('C4N3C4', substr($prefix, 0, 20)))."\n";
# constrain header
my ($dl,$d,$p,$res,$xd,$yd,$l0,$mx,$my,$order,$options,$rest) =
unpack('C4N3C4a*', $prefix);
redo if $xd * $yd > 1e9; # eliminate excessive image sizes
$prefix = pack('C4N3C4a*', $dl,$d,$p,$res,$xd,$yd,$l0,$mx,$my,
$order,$options,$rest);
print $out $prefix;
# append random suffix
my $data;
read $rnd, $data, $rnd_suffix_len;
print $out $data;
close($out) || die("$fntmp: $!\n");
rename($fntmp, $fntst) || die("mv $fntmp $fntst: $!\n");
# now feed fuzz input into decoder(s)
for my $jbgtopbm (@decoders) {
printf "%5d: ", $i;
$_ = `$pbmtools/$jbgtopbm $fntst /dev/null 2>&1`;
my $r = $?;
if ($r == 0) {
print "no error encountered\n";
next;
} elsif ($r == 256) {
my $err;
if (/(\(error code.*\))/) {
$err = $1;
print $err, "\n";
} else {
die("$_\nno error code found\n");
}
} else {
die("$_\nreturn value: $r\n");
}
}
}

View File

@@ -0,0 +1,114 @@
.TH JBGTOPBM 1 "2014-08-22"
.SH NAME
jbgtopbm \- JBIG1 to portable bitmap file converter
.SH SYNOPSIS
.B jbgtopbm
[
.I options
]
[
.I input-file
| \- [
.I output-file
]]
.br
.SH DESCRIPTION
Reads in a
.I JBIG1
bi-level image entity (BIE), decompresses it, and outputs a portable
bitmap (PBM) file.
.I JBIG1
is a highly effective lossless compression algorithm for
bi-level images (one bit per pixel), which is particularly suitable
for scanned document pages.
A
.I JBIG1
encoded image can be stored in several resolutions in one or several
BIEs. All resolution layers except the lowest one are stored
efficiently as differences to the next lower resolution layer. Options
.B -x
and
.B -y
can be used to stop the decompression at a specified maximal output
image size. With option
.B -m
the input file can consist of multiple concatenated BIEs
which contain different increasing resolution layers of the same
image.
If more than one bit per pixel is stored in the JBIG1 file, then a PGM
file will be produced.
.SH OPTIONS
.TP 12
.B \-
A single hyphen instead of an input file name will cause
.I jbgtopbm
to read the data from standard input instead from a file.
.TP
.BI \-x " number"
Decode only up to the largest resolution layer which is still not
more than
.I number
pixels wide. If no such resolution layer exists, then use the smallest
one available.
.TP
.BI \-y " number"
Decode only up to the largest resolution layer which is still not
more than
.I number
pixels high. If no such resolution layer exists, then use the smallest
one available. Options
.B \-x
and
.B \-y
can also be used together in which case the largest layer that satisfies
both limits will be selected.
.TP
.B \-m
Process multiple concatenated BIEs. If there are bytes left after the
final SDE in the first BIE, then with this option
.I jbgtopbm
will attempt to decode these as the start of another BIE that may
contain higher resolution data. Normally, any remaining bytes will
generate a warning message.
.TP
.B \-b
Use binary values instead of Gray code words in order to decode pixel
values from multiple bitplanes. This option has only an effect if the
input has more than one bitplane and a PGM output file is produced.
Note that the decoder has to be used in the same mode as the encoder
and cannot determine from the BIE, whether Gray or binary code words
were used by the encoder.
.TP
.B \-d
Diagnose a single BIE. With this option,
.I jbgtopbm
will print a summary of the header information found in the input
file, followed by a list of all PSCD and ESC marker sequences
encountered until the end of the file is reached.
.TP
.BI \-p " number"
If the input contains multiple bitplanes, then extract only the
specified single plane as a PBM file. The first plane has number 0.
.SH BUGS
Using standard input and standard output for binary data works only on
systems where there is no difference between binary and text streams
(e.g., Unix). On other systems (e.g., MS-DOS), using standard input or
standard output may cause control characters like CR or LF to be
inserted or deleted and this will damage the binary data.
.SH STANDARDS
This program implements the
.I JBIG1
image coding algorithm as specified in international standard ISO/IEC
11544:1993 and ITU-T Recommendation T.82(1993).
.SH AUTHOR
Markus Kuhn wrote
.UR http://www.cl.cam.ac.uk/~mgk25/jbigkit/
.I JBIG-KIT
.UE ,
which includes
.IR jbgtopbm .
.SH SEE ALSO
pbm(5), pgm(5), pbmtojbg(1)

View File

@@ -0,0 +1,489 @@
/*
* jbgtopbm - JBIG to Portable Bitmap converter
*
* Markus Kuhn - http://www.cl.cam.ac.uk/~mgk25/jbigkit/
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include "jbig.h"
char *progname; /* global pointer to argv[0] */
/*
* Print usage message and abort
*/
static void usage(void)
{
fprintf(stderr, "JBIGtoPBM converter " JBG_VERSION " -- "
"reads a bi-level image entity (BIE) as input file\n\n"
"usage: %s [<options>] [<input-file> | - [<output-file>]]\n\n"
"options:\n\n", progname);
fprintf(stderr,
" -x number\tif possible decode only up to a resolution layer not\n"
"\t\twider than the given number of pixels\n"
" -y number\tif possible decode only up to a resolution layer not\n"
"\t\thigher than the given number of pixels\n"
" -m\t\tdecode a progressive sequence of multiple concatenated BIEs\n"
" -b\t\tuse binary code for multiple bit planes (default: Gray code)\n"
" -d\t\tdiagnose single BIE, print header, list marker sequences\n"
" -p number\tdecode only one single bit plane (0 = first plane)\n\n");
exit(1);
}
/*
* Call-back routine for merged image output
*/
void write_it(unsigned char *data, size_t len, void *file)
{
fwrite(data, len, 1, (FILE *) file);
}
/*
* Remalloc a buffer and append a file f into its content.
* If *buflen == 0, then malloc a buffer first.
*/
void read_file(unsigned char **buf, size_t *buflen, size_t *len, FILE *f)
{
if (*buflen == 0) {
*buflen = 4000;
*len = 0;
*buf = (unsigned char *) malloc(*buflen);
if (!*buf) {
fprintf(stderr, "Sorry, not enough memory available!\n");
exit(1);
}
}
do {
*len += fread(*buf + *len, 1, *buflen - *len, f);
if (*len == *buflen) {
*buflen *= 2;
*buf = (unsigned char *) realloc(*buf, *buflen);
if (!*buf) {
fprintf(stderr, "Sorry, not enough memory available!\n");
exit(1);
}
}
if (ferror(f)) {
perror("Problem while reading input file");
exit(1);
}
} while (!feof(f));
if (!*len) return;
*buflen = *len;
*buf = (unsigned char *) realloc(*buf, *buflen);
if (!*buf) {
fprintf(stderr, "Oops, realloc failed when shrinking buffer!\n");
exit(1);
}
return;
}
/* marker codes */
#define MARKER_STUFF 0x00
#define MARKER_SDNORM 0x02
#define MARKER_SDRST 0x03
#define MARKER_ABORT 0x04
#define MARKER_NEWLEN 0x05
#define MARKER_ATMOVE 0x06
#define MARKER_COMMENT 0x07
#define MARKER_ESC 0xff
/*
* Output (prefix of) a short byte sequence in hexadecimal
* for diagnostic purposes
*/
void fprint_bytes(FILE *f, unsigned char *p, size_t len, int width)
{
size_t i;
size_t max = width / 3;
if (len > max)
max -= 7;
for (i = 0; i < len && i < max; i++)
fprintf(f, "%02x ", p[i]);
if (len > i)
fprintf(f, "... %lu bytes total", (unsigned long) len);
fprintf(f, "\n");
}
/*
* Read BIE and output human readable description of content
*/
void diagnose_bie(FILE *fin)
{
unsigned char *bie, *p, *pnext;
size_t buflen = 0, len;
unsigned long xd, yd, l0;
int dl, d;
FILE *f = stdout;
extern unsigned char *jbg_next_pscdms(unsigned char *p, size_t len);
extern unsigned long jbg_stripes(unsigned long l0, unsigned long yd,
unsigned long d);
unsigned long stripes;
int layers, planes;
unsigned long sdes, sde = 0;
double size;
/* read BIH */
read_file(&bie, &buflen, &len, fin);
if (len < 20) {
fprintf(f, "Error: Input file is %lu < 20 bytes long and therefore "
"does not contain an intact BIE header!\n", (unsigned long) len);
free(bie);
return;
}
/* parse BIH */
fprintf(f, "BIH:\n\n DL = %d\n D = %d\n P = %d\n"
" - = %d\n XD = %lu\n YD = %lu\n L0 = %lu\n MX = %d\n"
" MY = %d\n",
dl = bie[0], d = bie[1], planes = bie[2], bie[3],
xd = ((unsigned long)bie[ 4] << 24) | ((unsigned long)bie[ 5] << 16)|
((unsigned long) bie[ 6] << 8) | ((unsigned long) bie[ 7]),
yd = ((unsigned long)bie[ 8] << 24) | ((unsigned long)bie[ 9] << 16)|
((unsigned long) bie[10] << 8) | ((unsigned long) bie[11]),
l0 = ((unsigned long)bie[12] << 24) | ((unsigned long)bie[13] << 16)|
((unsigned long) bie[14] << 8) | ((unsigned long) bie[15]),
bie[16], bie[17]);
fprintf(f, " order = %d %s%s%s%s%s\n", bie[18],
bie[18] & JBG_HITOLO ? " HITOLO" : "",
bie[18] & JBG_SEQ ? " SEQ" : "",
bie[18] & JBG_ILEAVE ? " ILEAVE" : "",
bie[18] & JBG_SMID ? " SMID" : "",
bie[18] & 0xf0 ? " other" : "");
fprintf(f, " options = %d %s%s%s%s%s%s%s%s\n\n", bie[19],
bie[19] & JBG_LRLTWO ? " LRLTWO" : "",
bie[19] & JBG_VLENGTH ? " VLENGTH" : "",
bie[19] & JBG_TPDON ? " TPDON" : "",
bie[19] & JBG_TPBON ? " TPBON" : "",
bie[19] & JBG_DPON ? " DPON" : "",
bie[19] & JBG_DPPRIV ? " DPPRIV" : "",
bie[19] & JBG_DPLAST ? " DPLAST" : "",
bie[19] & 0x80 ? " other" : "");
if (d < dl) { fprintf(f, "D >= DL required!\n"); return; }
if (l0 == 0) { fprintf(f, "L0 > 0 required!\n"); return; }
stripes = jbg_stripes(l0, yd, d);
layers = d - dl + 1;
fprintf(f, " %lu stripes, %d layers, %d planes => ",
stripes, layers, planes);
if (planes == 0 || (ULONG_MAX / layers) / planes >= stripes) {
sdes = stripes * layers * planes;
fprintf(f, "%lu SDEs\n", sdes);
} else {
/* handle integer overflow */
fprintf(f, ">%lu SDEs!\n", ULONG_MAX);
return;
}
size = (double) planes * (double) yd * (double) jbg_ceil_half(xd, 3);
fprintf(f, " decompressed %.15g bytes\n\n", size);
if (planes == 0) { fprintf(f, "P > 0 required!\n\n"); }
/* parse BID */
fprintf(f, "BID:\n\n");
p = bie + 20; /* skip BIH */
if ((bie[19] & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST))
== (JBG_DPON | JBG_DPPRIV))
p += 1728; /* skip DPTABLE */
if (p > bie + len) {
fprintf(f, "Error: Input file is %lu < 20+1728 bytes long and therefore "
"does not contain an intact BIE header with DPTABLE!\n",
(unsigned long) len);
return;
}
while (p != bie + len) {
if (p > bie + len - 2) {
fprintf(f, "%06lx: Error: single byte 0x%02x left\n",
(long) (p - bie), *p);
return;
}
pnext = jbg_next_pscdms(p, len - (p - bie));
if (p[0] != MARKER_ESC || p[1] == MARKER_STUFF) {
fprintf(f, "%06lx: PSCD: ", (long) (p - bie));
fprint_bytes(f, p, pnext ? (size_t) (pnext - p) : len - (p - bie), 60);
if (!pnext) {
fprintf(f, "Error: PSCD not terminated by SDNORM or SDRST marker\n");
return;
}
} else
switch (p[1]) {
case MARKER_SDNORM:
case MARKER_SDRST:
fprintf(f, "%06lx: ESC %s, ending SDE #%lu", (long) (p - bie),
(p[1] == MARKER_SDNORM) ? "SDNORM" : "SDRST", ++sde);
if (sde == sdes)
fprintf(f, " (final SDE)");
else if (sde == sdes + 1)
fprintf(f, " (first surplus SDE, VLENGTH = %d)",
(bie[19] & JBG_VLENGTH) > 0);
fprintf(f, "\n");
break;
case MARKER_ABORT:
fprintf(f, "%06lx: ESC ABORT\n", (long) (p - bie));
break;
case MARKER_NEWLEN:
fprintf(f, "%06lx: ESC NEWLEN ", (long) (p - bie));
if (p + 5 < bie + len) {
fprintf(f, "YD = %lu\n",
yd = (((long) p[2] << 24) | ((long) p[3] << 16) |
((long) p[4] << 8) | (long) p[5]));
stripes = jbg_stripes(l0, yd, d);
fprintf(f, " %lu stripes, %d layers, %d planes => ",
stripes, layers, planes);
if ((ULONG_MAX / layers) / planes >= stripes) {
sdes = stripes * layers * planes;
fprintf(f, "%lu SDEs\n", sdes);
} else {
/* handle integer overflow */
fprintf(f, ">%lu SDEs!\n", ULONG_MAX);
return;
}
} else
fprintf(f, "unexpected EOF\n");
break;
case MARKER_ATMOVE:
fprintf(f, "%06lx: ESC ATMOVE ", (long) (p - bie));
if (p + 7 < bie + len)
fprintf(f, "YAT = %lu, tX = %d, tY = %d\n",
(((long) p[2] << 24) | ((long) p[3] << 16) |
((long) p[4] << 8) | (long) p[5]), p[6], p[7]);
else
fprintf(f, "unexpected EOF\n");
break;
case MARKER_COMMENT:
fprintf(f, "%06lx: ESC COMMENT ", (long) (p - bie));
if (p + 5 < bie + len)
fprintf(f, "LC = %lu\n",
(((long) p[2] << 24) | ((long) p[3] << 16) |
((long) p[4] << 8) | (long) p[5]));
else
fprintf(f, "unexpected EOF\n");
break;
default:
fprintf(f, "%06lx: ESC 0x%02x\n", (long) (p - bie), p[1]);
}
if (!pnext) {
fprintf(f, "Error encountered!\n");
return;
}
p = pnext;
}
free(bie);
return;
}
int main (int argc, char **argv)
{
FILE *fin = stdin, *fout = stdout;
const char *fnin = NULL, *fnout = NULL;
int i, j, result;
int all_args = 0, files = 0;
struct jbg_dec_state s;
unsigned char *buffer, *p;
size_t buflen, len, cnt;
size_t bytes_read = 0;
unsigned long xmax = 4294967295UL, ymax = 4294967295UL, max;
int plane = -1, use_graycode = 1, diagnose = 0, multi = 0;
buflen = 8000;
buffer = (unsigned char *) malloc(buflen);
if (!buffer) {
printf("Sorry, not enough memory available!\n");
exit(1);
}
/* parse command line arguments */
progname = argv[0];
for (i = 1; i < argc; i++) {
if (!all_args && argv[i][0] == '-')
if (argv[i][1] == 0) {
if (files++) usage();
} else
for (j = 1; j > 0 && argv[i][j]; j++)
switch(argv[i][j]) {
case '-' :
all_args = 1;
break;
case 'b':
use_graycode = 0;
break;
case 'm':
multi = 1;
break;
case 'd':
diagnose = 1;
break;
case 'x':
if (++i >= argc) usage();
xmax = atol(argv[i]);
j = -1;
break;
case 'y':
if (++i >= argc) usage();
ymax = atol(argv[i]);
j = -1;
break;
case 'p':
if (++i >= argc) usage();
plane = atoi(argv[i]);
j = -1;
break;
default:
usage();
}
else
switch (files++) {
case 0: fnin = argv[i]; break;
case 1: fnout = argv[i]; break;
default:
usage();
}
}
if (fnin) {
fin = fopen(fnin, "rb");
if (!fin) {
fprintf(stderr, "Can't open input file '%s", fnin);
perror("'");
exit(1);
}
} else
fnin = "<stdin>";
if (diagnose) {
diagnose_bie(fin);
exit(0);
}
if (fnout) {
fout = fopen(fnout, "wb");
if (!fout) {
fprintf(stderr, "Can't open input file '%s", fnout);
perror("'");
exit(1);
}
} else
fnout = "<stdout>";
/* send input file to decoder */
jbg_dec_init(&s);
jbg_dec_maxsize(&s, xmax, ymax);
/* read BIH first to check VLENGTH */
len = fread(buffer, 1, 20, fin);
if (len < 20) {
fprintf(stderr, "Input file '%s' (%lu bytes) must be at least "
"20 bytes long\n", fnin, (unsigned long) len);
if (fout != stdout) {
fclose(fout);
remove(fnout);
}
exit(1);
}
if (buffer[19] & JBG_VLENGTH) {
/* VLENGTH = 1 => we might encounter a NEWLEN, therefore read entire
* input file into memory and run two passes over it */
read_file(&buffer, &buflen, &len, fin);
/* scan for NEWLEN marker segments and update BIE header accordingly */
result = jbg_newlen(buffer, len);
/* feed data to decoder */
if (result == JBG_EOK) {
p = (unsigned char *) buffer;
result = JBG_EAGAIN;
while (len > 0 &&
(result == JBG_EAGAIN || (result == JBG_EOK && multi))) {
result = jbg_dec_in(&s, p, len, &cnt);
p += cnt;
len -= cnt;
bytes_read += cnt;
}
}
} else {
/* VLENGTH = 0 => we can simply pass the input file directly to decoder */
result = JBG_EAGAIN;
do {
cnt = 0;
p = (unsigned char *) buffer;
while (len > 0 &&
(result == JBG_EAGAIN || (result == JBG_EOK && multi))) {
result = jbg_dec_in(&s, p, len, &cnt);
p += cnt;
len -= cnt;
bytes_read += cnt;
}
if (!(result == JBG_EAGAIN || (result == JBG_EOK && multi)))
break;
len = fread(buffer, 1, buflen, fin);
} while (len > 0);
if (ferror(fin)) {
fprintf(stderr, "Problem while reading input file '%s", fnin);
perror("'");
if (fout != stdout) {
fclose(fout);
remove(fnout);
}
exit(1);
}
}
if (result != JBG_EOK && result != JBG_EOK_INTR) {
fprintf(stderr, "Problem with input file '%s': %s\n"
"(error code 0x%02x, %lu = 0x%04lx BIE bytes processed)\n",
fnin, jbg_strerror(result), result,
(unsigned long) bytes_read, (unsigned long) bytes_read);
if (fout != stdout) {
fclose(fout);
remove(fnout);
}
exit(1);
}
if (plane >= 0 && jbg_dec_getplanes(&s) <= plane) {
fprintf(stderr, "Image has only %d planes!\n", jbg_dec_getplanes(&s));
if (fout != stdout) {
fclose(fout);
remove(fnout);
}
exit(1);
}
if (jbg_dec_getplanes(&s) == 1 || plane >= 0) {
/* write PBM output file */
fprintf(fout, "P4\n%10lu\n%10lu\n", jbg_dec_getwidth(&s),
jbg_dec_getheight(&s));
fwrite(jbg_dec_getimage(&s, plane < 0 ? 0 : plane), 1,
jbg_dec_getsize(&s), fout);
} else {
/* write PGM output file */
if ((size_t) jbg_dec_getplanes(&s) > sizeof(unsigned long) * 8) {
fprintf(stderr, "Image has too many planes (%d)!\n",
jbg_dec_getplanes(&s));
if (fout != stdout) {
fclose(fout);
remove(fnout);
}
exit(1);
}
max = 0;
for (i = jbg_dec_getplanes(&s); i > 0; i--)
max = (max << 1) | 1;
fprintf(fout, "P5\n%10lu\n%10lu\n%lu\n", jbg_dec_getwidth(&s),
jbg_dec_getheight(&s), max);
jbg_dec_merge_planes(&s, use_graycode, write_it, fout);
}
/* check for file errors and close fout */
if (ferror(fout) || fclose(fout)) {
fprintf(stderr, "Problem while writing output file '%s", fnout);
perror("'");
exit(1);
}
jbg_dec_free(&s);
return 0;
}

View File

@@ -0,0 +1,205 @@
/*
* jbgtopbm85 - JBIG to Portable Bitmap converter (T.85 version)
*
* Markus Kuhn - http://www.cl.cam.ac.uk/~mgk25/jbigkit/
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include "jbig85.h"
char *progname; /* global pointer to argv[0] */
unsigned long y_0;
fpos_t ypos;
int ypos_error = 1;
unsigned long ymax = 0;
/*
* Print usage message and abort
*/
static void usage(void)
{
fprintf(stderr, "JBIGtoPBM converter " JBG85_VERSION " (T.85 version) --\n"
"reads a bi-level image entity (BIE) as input file\n\n"
"usage: %s [<input-file> | - [<output-file>]]\n\n", progname);
fprintf(stderr, "options:\n\n"
" -x number\tmaximum number of pixels per line for which memory\n"
"\t\tis allocated (default: 8192)\n"
" -y number\tinterrupt decoder after this number of lines\n"
" -B number\tinput buffer size\n\n");
exit(1);
}
/*
* Call-back routine for merged image output
*/
int line_out(const struct jbg85_dec_state *s,
unsigned char *start, size_t len, unsigned long y, void *file)
{
if (y == 0) {
/* prefix first line with PBM header */
fprintf((FILE *) file, "P4\n");
fprintf((FILE *) file, "%10lu\n", jbg85_dec_getwidth(s));
/* store file position of height, so we can update it after NEWLEN */
y_0 = jbg85_dec_getheight(s);
ypos_error = fgetpos((FILE *) file, &ypos);
fprintf((FILE *) file, "%10lu\n", y_0); /* pad number to 10 bytes */
}
fwrite(start, len, 1, (FILE *) file);
return y == ymax - 1;
}
int main (int argc, char **argv)
{
FILE *fin = stdin, *fout = stdout;
const char *fnin = NULL, *fnout = NULL;
int i, j, result;
int all_args = 0, files = 0;
struct jbg85_dec_state s;
unsigned char *inbuf, *outbuf;
size_t inbuflen = 8192, outbuflen, len, cnt, cnt2;
unsigned long xmax = 8192;
size_t bytes_read = 0;
/* parse command line arguments */
progname = argv[0];
for (i = 1; i < argc; i++) {
if (!all_args && argv[i][0] == '-')
if (argv[i][1] == 0) {
if (files++) usage();
} else
for (j = 1; j > 0 && argv[i][j]; j++)
switch(argv[i][j]) {
case '-' :
all_args = 1;
break;
case 'x':
if (++i >= argc) usage();
j = -1;
xmax = atol(argv[i]);
break;
case 'y':
if (++i >= argc) usage();
j = -1;
ymax = atol(argv[i]);
break;
case 'B':
if (++i >= argc) usage();
j = -1;
inbuflen = atol(argv[i]);
if (inbuflen < 1) usage();
break;
default:
usage();
}
else
switch (files++) {
case 0: fnin = argv[i]; break;
case 1: fnout = argv[i]; break;
default:
usage();
}
}
inbuf = (unsigned char *) malloc(inbuflen);
outbuflen = ((xmax >> 3) + !!(xmax & 7)) * 3;
outbuf = (unsigned char *) malloc(outbuflen);
if (!inbuf || !outbuf) {
printf("Sorry, not enough memory available!\n");
exit(1);
}
if (fnin) {
fin = fopen(fnin, "rb");
if (!fin) {
fprintf(stderr, "Can't open input file '%s", fnin);
perror("'");
exit(1);
}
} else
fnin = "<stdin>";
if (fnout) {
fout = fopen(fnout, "wb");
if (!fout) {
fprintf(stderr, "Can't open input file '%s", fnout);
perror("'");
exit(1);
}
} else
fnout = "<stdout>";
/* send input file to decoder */
jbg85_dec_init(&s, outbuf, outbuflen, line_out, fout);
result = JBG_EAGAIN;
while ((len = fread(inbuf, 1, inbuflen, fin))) {
result = jbg85_dec_in(&s, inbuf, len, &cnt);
bytes_read += cnt;
while (result == JBG_EOK_INTR) {
/* demonstrate decoder interrupt at given line number */
printf("Decoding interrupted after %lu lines and %lu BIE bytes "
"... continuing ...\n", s.y, (unsigned long) bytes_read);
/* and now continue decoding */
result = jbg85_dec_in(&s, inbuf + cnt, len - cnt, &cnt2);
bytes_read += cnt2;
cnt += cnt2;
}
if (result != JBG_EAGAIN)
break;
}
if (ferror(fin)) {
fprintf(stderr, "Problem while reading input file '%s", fnin);
perror("'");
if (fout != stdout) {
fclose(fout);
remove(fnout);
}
exit(1);
}
if (result == JBG_EAGAIN || result == JBG_EOK_INTR) {
/* signal end-of-BIE explicitely */
result = jbg85_dec_end(&s);
while (result == JBG_EOK_INTR) {
/* demonstrate decoder interrupt at given line number */
printf("Decoding interrupted after %lu lines and %lu BIE bytes "
"... continuing ...\n", s.y, (unsigned long) bytes_read);
result = jbg85_dec_end(&s);
}
}
if (result != JBG_EOK) {
fprintf(stderr, "Problem with input file '%s': %s\n"
"(error code 0x%02x, %lu = 0x%04lx BIE bytes "
"and %lu pixel rows processed)\n",
fnin, jbg85_strerror(result), result,
(unsigned long) bytes_read, (unsigned long) bytes_read, s.y);
if (fout != stdout) {
fclose(fout);
/*remove(fnout);*/
}
exit(1);
}
/* do we have to update the image height in the PBM header? */
if (!ypos_error && y_0 != jbg85_dec_getheight(&s)) {
if (fsetpos(fout, &ypos) == 0) {
fprintf(fout, "%10lu", jbg85_dec_getheight(&s)); /* pad to 10 bytes */
} else {
fprintf(stderr, "Problem while updating height in output file '%s",
fnout);
perror("'");
exit(1);
}
}
/* check for file errors and close fout */
if (ferror(fout) || fclose(fout)) {
fprintf(stderr, "Problem while writing output file '%s", fnout);
perror("'");
exit(1);
}
return 0;
}

View File

@@ -0,0 +1,91 @@
.TH pbm 5 "27 September 1991"
.SH NAME
pbm - portable bitmap file format
.SH DESCRIPTION
The portable bitmap format is a lowest common denominator monochrome
file format.
.IX "PBM file format"
It was originally designed to make it reasonable to mail bitmaps
between different types of machines using the typical stupid network
mailers we have today.
Now it serves as the common language of a large family of bitmap
conversion filters.
The definition is as follows:
.IP - 2
A "magic number" for identifying the file type.
A pbm file's magic number is the two characters "P1".
.IX "magic numbers"
.IP - 2
Whitespace (blanks, TABs, CRs, LFs).
.IP - 2
A width, formatted as ASCII characters in decimal.
.IP - 2
Whitespace.
.IP - 2
A height, again in ASCII decimal.
.IP - 2
Whitespace.
.IP - 2
Width * height bits, each either '1' or '0', starting at the top-left
corner of the bitmap, proceeding in normal English reading order.
.IP - 2
The character '1' means black, '0' means white.
.IP - 2
Whitespace in the bits section is ignored.
.IP - 2
Characters from a "#" to the next end-of-line are ignored (comments).
.IP - 2
No line should be longer than 70 characters.
.PP
Here is an example of a small bitmap in this format:
.nf
P1
# feep.pbm
24 7
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0
0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 0
0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0
0 1 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
.fi
.PP
Programs that read this format should be as lenient as possible,
accepting anything that looks remotely like a bitmap.
.PP
There is also a variant on the format, available
by setting the RAWBITS option at compile time. This variant is
.IX RAWBITS
different in the following ways:
.IP - 2
The "magic number" is "P4" instead of "P1".
.IP - 2
The bits are stored eight per byte, high bit first low bit last.
.IP - 2
No whitespace is allowed in the bits section, and only a single character
of whitespace (typically a newline) is allowed after the height.
.IP - 2
The files are eight times smaller and many times faster to read and write.
.SH "SEE ALSO"
atktopbm(1), brushtopbm(1), cmuwmtopbm(1), g3topbm(1),
gemtopbm(1), icontopbm(1),
macptopbm(1), mgrtopbm(1), pi3topbm(1), xbmtopbm(1),
ybmtopbm(1),
pbmto10x(1), pnmtoascii(1), pbmtoatk(1), pbmtobbnbg(1),
pbmtocmuwm(1), pbmtoepson(1),
pbmtog3(1), pbmtogem(1), pbmtogo(1), pbmtoicon(1), pbmtolj(1),
pbmtomacp(1), pbmtomgr(1), pbmtopi3(1), pbmtoplot(1), pbmtoptx(1),
pbmtox10bm(1), pbmtoxbm(1), pbmtoybm(1),
pbmtozinc(1),
pbmlife(1), pbmmake(1), pbmmask(1), pbmreduce(1),
pbmtext(1), pbmupc(1),
pnm(5), pgm(5), ppm(5)
.SH AUTHOR
Copyright (C) 1989, 1991 by Jef Poskanzer.
.\" Permission to use, copy, modify, and distribute this software and its
.\" documentation for any purpose and without fee is hereby granted, provided
.\" that the above copyright notice appear in all copies and that both that
.\" copyright notice and this permission notice appear in supporting
.\" documentation. This software is provided "as is" without express or
.\" implied warranty.

View File

@@ -0,0 +1,306 @@
.TH PBMTOJBG 1 "2014-08-22"
.SH NAME
pbmtojbg \- portable bitmap to JBIG1 file converter
.SH SYNOPSIS
.B pbmtojbg
[
.I options
]
[
.I input-file
| \- [
.I output-file
]]
.br
.SH DESCRIPTION
Reads a file in portable bitmap (PBM) format, compresses it,
and outputs the image as a
.I JBIG1
bi-level image entity (BIE).
.I JBIG1
is a highly effective, lossless compression algorithm for
bi-level images (one bit per pixel), which is particularly suitable
for scanned document pages.
A
.I JBIG1
encoded image can be stored in several resolution layers (progressive mode).
This allows the decoder to recover a lower-resolution version of the
encoded image without having to read the complete file.
These resolution layers can be stored all in one single BIE or they
can be stored in several separate BIE files.
All resolution layers, except the lowest one, are stored merely as
differences to the next lower resolution layer. This requires less
space than encoding the full image completely every time. Each resolution
layer has twice the number of horizontal and vertical pixels than
the next lower layer.
For best speed and compression, if you are not interested in
efficiently extracting lower-resolution versions from the compressed
file, use sequential mode (single resolution layer, option
.BR \-q ).
.I JBIG1
files can also store several bits per pixel, as separate bitmap planes,
and
.I pbmtojbg
can read a portable graymap format (PGM) file and transform it into
a multi-plane BIE.
.SH OPTIONS
.TP 12
.BI \-
A single hyphen instead of an input file name causes
.I pbmtojbg
to read the data from standard input instead of from a file.
.TP
.BI \-v
List technical details of the created file (verbose mode).
.TP
.BI \-f
This option makes the output file comply with the \[lq]facsimile application
profile\[rq] defined in ITU-T Recommendation T.85. It is a shortcut for
.BR "-q -o 0 -p 8 -s 128 -t 1 -m 127" .
.TP
.BI \-C " string"
Add the
.I string
in a comment marker segment to the produced data stream.
.br
(There is no support at present for adding comments that contain a
zero byte.)
.P
Options affecting the number of resolution layers produced:
.TP 12
.BI \-q
Encode the image in one single resolution layer (sequential mode), which
is usually the most efficient compression method. By default, the number
of resolution layers is instead chosen automatically such that the lowest layer
image is not larger than 640 \(mu 480 pixels.
This option is just a shortcut for
.BR "-d 0" .
.TP
.BI \-x " number"
Specify the maximal horizontal size of the lowest resolution layer.
Default: 640 pixels
.TP
.BI \-y " number"
Specify the maximal vertical size of the lowest resolution layer.
Default: 480 pixels
.TP
.BI \-d " number"
Specify the total number of differential resolution layers into which to split
the input image (in addition to the lowest layer). Each additional
layer reduces both the width and height of layer 0 by 50%.
This option overrides options
.B \-x
and
.BR \-y ,
which are usually a more convenient way of selecting the number of
resolution layers.
.TP
.BI \-l " number"
Select the lowest resolution layer
.I D\s-2\dL\u\s0
that will appear in the generated
BIE.
.I JBIG1
can store the various resolution layers of an image in progressive mode
split across several BIEs. Options
.B \-l
and
.B \-h
allow you to select the resolution-layer interval that will appear
in the created BIE. The lowest resolution layer has number 0 and
this is also the default value. The default is to write all layers.
.TP
.BI \-h " number"
Select the highest resolution layer
.I D
that will appear in the generated BIE. The default is to write all layers.
See also option
.BR \-l .
.P
Options for changing other parameters indicated in the BIE header:
.TP 12
.BI \-s " number"
The
.I JBIG1
algorithm splits each image into horizontal stripes. This
option specifies
.IR L\s-2\d0\u\s0 ,
the layer-0 height of each such stripe (except for the last one,
which can be shorter).
The default is to split the whole image into approximately 35 stripes.
.TP
.BI \-m " number"
Select the maximum horizontal offset
.I M\s-2\dX\u\s0
of the adaptive template pixel.
The
.I JBIG1
encoder uses ten neighbour pixels to estimate the probability of the
next pixel being black or white. It can adjust the location of one
of these ten context pixels.
This is especially useful for dithered images, as long as the
distance of this adaptive pixel can be adjusted to the period of the
dither pattern. By default, the adaptive template pixel is allowed to
move up to 8 pixels away horizontally. This encoder supports distances
up to 127 pixels.
.br
Annex A of the standard suggests that decoders should support at least
a horizontal distance of 16 pixels, so using values not higher than 16
for
.I number
might increase the chances of interoperability with other
.I JBIG1
implementations. On the other hand, the T.85 fax application profile
requires decoders to support horizontal offsets up to 127 pixels,
which is the maximum value permitted by the standard. (The maximal
vertical offset
.I M\s-2\dY\u\s0
of the adaptive template pixel is always zero for this
encoder.)
.TP
.BI \-o " number"
Specify the order in which image data appears in the output.
.I JBIG1
separates an image into several horizontal stripes, resolution layers
and planes, were each plane contains one bit per pixel. One single
stripe in one plane and layer is encoded as a data unit called stripe
data entity (SDE) inside the BIE. There are 12 different possible
orders in which the SDEs can be stored inside the BIE and
.I number
selects which one shall be used. For receiving applications, the order of
the SDEs mainly matters if they want to start decoding an image before
it has been received completely.
For instance, some applications may prefer that the outermost of the
three loops (stripes, layers, planes) is over all layers so that all
data of the lowest resolution layer is transmitted first.
.br
The following values for
.I number
select these loop arrangements for writing the SDEs (outermost
loop first):
0 planes, layers, stripes
.br
2 layers, planes, stripes
.br
3 layers, stripes, planes
.br
4 stripes, planes, layers
.br
5 planes, stripes, layers
.br
6 stripes, layers, planes
All loops count starting with zero, however by adding 8 to the above
order code, the layer loop can be reversed so that it counts down to zero
and then higher resolution layers will be stored before lower layers.
The default order is 3, which writes at first all planes of the first
stripe and then completes layer 0 before continuing with the next
layer, and so on.
.TP
.BI \-p " number"
This option activates or deactivates various optional algorithms
defined in the
.I JBIG1
standard. Just add the numbers of the following options which you want to
activate in
order to get the
.I number
value:
4 deterministic prediction (DPON)
.br
8 layer 0 typical prediction (TPBON)
.br
16 diff. layer typ. pred. (TPDON)
.br
64 layer 0 two-line template (LRLTWO)
This option is only for specialist applications (e.g., communication with
.I JBIG1
subset implementations, debugging). The default is 28, which usually
provides the best compression result.
.P
Options affecting the processing of PGM files:
.TP 12
.BI \-b
Use binary values instead of Gray code words in order to encode pixel
values in multiple bit planes. This option has only an effect if the
input is a PGM file and if more than one plane is produced. Note that
the decoder has to make the same choice, but the BIE does not
indicate whether Gray or binary code words were used by the encoder.
.TP
.BI \-t " number"
Encode only the specified number of most significant bit planes. This
option reduces the depth of an input PGM file if not all
bits per pixel are needed in the output.
.P
Options to help generating test files:
.TP 12
.BI \-r
Use the SDRST marker instead of the normal SDNORM marker. The probably
only useful application for this option is to generate test data for
checking whether a
.I JBIG1
decoder has implemented SDRST correctly. In a normal
.I JBIG1
data stream, each stripe data entity (SDE) is terminated by an SDNORM
marker, which preserves the state of the arithmetic encoder (and more)
for the next stripe in the same layer. The alternative SDRST marker
resets this state at the end of the stripe.
.TP
.BI \-Y " number"
Specify a preliminary image height
.IR Y\s-2\dD\u\s0 .
A long time ago, there were fax
machines that couldn't even hold a single page in memory. They had to
start transmitting data before the page was scanned in completely and
before even the height of the resulting image was known.
The authors of the standard added a rather ugly hack to JBIG1
to support this use case. The NEWLEN marker segment can
override the image height stated in the BIE header anywhere later in
the data stream. Normally
.I pbmtojbg
never generates NEWLEN marker segments, as it knows the correct image
height when it outputs the header. This option is solely intended for
the purpose of generating test files with NEWLEN marker segments. It
can be used to specify a higher initial image height for use in the
BIE header, and
.I pbmtojbg
will then add a NEWLEN marker segment at the latest possible
opportunity to the data stream to signal the correct final height.
It will also set the VLENGTH flag in the BIE header.
.TP
.BI \-c
Determine the adaptive template pixel movement as suggested in Annex C
of the standard. By default the template change takes place directly
in the next line, which is most effective. However, a few conformance
test examples in the standard require the adaptive template change to
be delayed until the first line of the next stripe. This option
selects this special behavior, which is normally not useful other than
for conformance testing.
.SH BUGS
Using standard input and standard output for binary data works only on
systems where there is no difference between binary and text streams
(e.g., Unix). On other systems (e.g., MS-DOS), using standard input or
standard output may cause control characters like CR or LF to be
inserted or deleted and this will damage the binary data.
.SH STANDARDS
This program implements the
.I JBIG1
image coding algorithm as specified in international standard ISO/IEC
11544:1993 and ITU-T Recommendation T.82(1993).
.SH AUTHOR
Markus Kuhn wrote
.UR http://www.cl.cam.ac.uk/~mgk25/jbigkit/
.I JBIG-KIT
.UE ,
which includes
.IR pbmtojbg .
.SH SEE ALSO
pbm(5), pgm(5), jbgtopbm(1)

View File

@@ -0,0 +1,455 @@
/*
* pbmtojbg - Portable Bitmap to JBIG converter
*
* Markus Kuhn - http://www.cl.cam.ac.uk/~mgk25/jbigkit/
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include "jbig.h"
char *progname; /* global pointer to argv[0] */
unsigned long total_length = 0; /* used for determining output file length */
/*
* Print usage message and abort
*/
static void usage(void)
{
fprintf(stderr,
"PBMtoJBIG converter " JBG_VERSION " -- "
"creates bi-level image entity (BIE) as output file\n\n"
"usage: %s [<options>] [<input-file> | - [<output-file>]]\n\n"
"options:\n\n", progname);
fprintf(stderr,
" -q\t\tsequential coding, no differential layers (like -d 0)\n"
" -x number\tmaximum width of lowest resolution layer (default 640)\n"
" -y number\tmaximum height of lowest resolution layer (default 480)\n"
" -l number\tlowest layer written to output file (default 0)\n"
" -h number\thighest layer written to output file (default max)\n"
" -b\t\tuse binary code for multiple bitplanes (default: Gray code)\n"
" -d number\ttotal number of differential layers (overrides -x and -y)\n"
" -s number\theight of a stripe in layer 0\n");
fprintf(stderr,
" -m number\tmaximum adaptive template pixel horizontal offset (default 8)\n"
" -t number\tencode only that many most significant planes\n"
" -o number\torder byte value: add 1=SMID, 2=ILEAVE, 4=SEQ, 8=HITOLO\n"
"\t\t(default 3 = ILEAVE+SMID)\n"
" -p number\toptions byte value: add DPON=4, TPBON=8, TPDON=16, LRLTWO=64\n"
"\t\t(default 28 = DPON+TPBON+TPDON)\n");
fprintf(stderr,
" -C string\tadd the provided string as a comment marker segment\n"
" -c\t\tdelay adaptive template changes to first line of next stripe\n"
"\t\t(only provided for a conformance test)\n"
" -r\t\tterminate each stripe with SDRST marker\n"
"\t\t(only intended for decoder testing)\n" );
fprintf(stderr,
" -Y number\tannounce in header initially this larger image height\n"
"\t\t(only for generating test files with NEWLEN and VLENGTH=1)\n"
" -f\t\tchose encoding options for T.85 fax profile complianance\n"
" -v\t\tverbose output\n\n");
exit(1);
}
/*
* malloc() with exception handler
*/
void *checkedmalloc(size_t n)
{
void *p;
if ((p = malloc(n)) == NULL) {
fprintf(stderr, "Sorry, not enough memory available!\n");
exit(1);
}
return p;
}
/*
* Read an ASCII integer number from file f and skip any PBM
* comments which are encountered.
*/
static unsigned long getint(FILE *f)
{
int c;
unsigned long i;
while ((c = getc(f)) != EOF)
if (c == '#')
while ((c = getc(f)) != EOF && !(c == 13 || c == 10)) ;
else if (!isspace(c))
break;
if (c == EOF) return 0;
ungetc(c, f);
if (fscanf(f, "%lu", &i) != 1) {
fprintf(stderr, "Unsigned integer value expected!\n");
exit(1);
}
return i;
}
/*
* Callback procedure which is used by JBIG encoder to deliver the
* encoded data. It simply sends the bytes to the output file.
*/
static void data_out(unsigned char *start, size_t len, void *file)
{
fwrite(start, len, 1, (FILE *) file);
total_length += len;
return;
}
int main (int argc, char **argv)
{
FILE *fin = stdin, *fout = stdout;
const char *fnin = NULL, *fnout = NULL;
int i, j, c;
int all_args = 0, files = 0;
unsigned long x, y;
unsigned long width, height, max, v;
unsigned long bpl;
int bpp, planes, encode_planes = -1;
size_t bitmap_size;
char type;
unsigned char **bitmap, *p, *image;
struct jbg_enc_state s;
int verbose = 0, delay_at = 0, reset = 0, use_graycode = 1;
long mwidth = 640, mheight = 480;
int dl = -1, dh = -1, d = -1, mx = -1;
unsigned long l0 = 0, y1 = 0;
char *comment = NULL;
int options = JBG_TPDON | JBG_TPBON | JBG_DPON;
int order = JBG_ILEAVE | JBG_SMID;
/* parse command line arguments */
progname = argv[0];
for (i = 1; i < argc; i++) {
if (!all_args && argv[i][0] == '-')
if (argv[i][1] == 0) {
if (files++) usage();
} else
for (j = 1; j > 0 && argv[i][j]; j++)
switch(argv[i][j]) {
case '-' :
all_args = 1;
break;
case 0 :
if (files++) usage();
break;
case 'v':
verbose = 1;
break;
case 'b':
use_graycode = 0;
break;
case 'c':
delay_at = 1;
break;
case 'r':
reset = 1;
break;
case 'f':
d = 0;
order = 0;
options = 8;
l0 = 128;
encode_planes = 1;
mx = 127;
break;
case 'x':
if (++i >= argc) usage();
j = -1;
mwidth = atol(argv[i]);
break;
case 'y':
if (++i >= argc) usage();
j = -1;
mheight = atol(argv[i]);
break;
case 'Y':
if (++i >= argc) usage();
j = -1;
y1 = atol(argv[i]);
break;
case 'o':
if (++i >= argc) usage();
j = -1;
order = atoi(argv[i]);
break;
case 'p':
if (++i >= argc) usage();
j = -1;
options = atoi(argv[i]);
break;
case 'l':
if (++i >= argc) usage();
j = -1;
dl = atoi(argv[i]);
break;
case 'h':
if (++i >= argc) usage();
j = -1;
dh = atoi(argv[i]);
break;
case 'q':
d = 0;
break;
case 'd':
if (++i >= argc) usage();
j = -1;
d = atoi(argv[i]);
break;
case 's':
if (++i >= argc) usage();
j = -1;
l0 = atol(argv[i]);
break;
case 't':
if (++i >= argc) usage();
j = -1;
encode_planes = atoi(argv[i]);
break;
case 'm':
if (++i >= argc) usage();
j = -1;
mx = atoi(argv[i]);
break;
case 'C':
if (++i >= argc) usage();
j = -1;
comment = argv[i];
break;
default:
usage();
}
else
switch (files++) {
case 0: fnin = argv[i]; break;
case 1: fnout = argv[i]; break;
default:
usage();
}
}
if (fnin) {
fin = fopen(fnin, "rb");
if (!fin) {
fprintf(stderr, "Can't open input file '%s", fnin);
perror("'");
exit(1);
}
} else
fnin = "<stdin>";
if (fnout) {
fout = fopen(fnout, "wb");
if (!fout) {
fprintf(stderr, "Can't open input file '%s", fnout);
perror("'");
exit(1);
}
} else
fnout = "<stdout>";
/* read PBM header */
while ((c = getc(fin)) != EOF && (isspace(c) || c == '#'))
if (c == '#')
while ((c = getc(fin)) != EOF && !(c == 13 || c == 10)) ;
if (c != 'P') {
fprintf(stderr, "Input file '%s' does not look like a PBM file!\n", fnin);
exit(1);
}
type = getc(fin);
width = getint(fin);
height = getint(fin);
if (width < 1 || height < 1 ||
width > 0xffffffff || height > 0xffffffff) {
fprintf(stderr, "Invalid width or height!\n");
exit(1);
}
if (type == '2' || type == '5' ||
type == '3' || type == '6')
max = getint(fin);
else
max = 1;
if (max < 1) {
fprintf(stderr, "Invalid maxval!\n");
exit(1);
}
for (planes = 0, v = max; v; planes++, v >>= 1) ;
bpp = (planes + 7) / 8;
if (encode_planes < 1 || encode_planes > planes)
encode_planes = planes;
fgetc(fin); /* skip line feed */
/* read PBM image data */
bpl = jbg_ceil_half(width, 3); /* bytes per line */
if (ULONG_MAX / height < bpl) {
fprintf(stderr, "Image too large!\n");
exit(1);
}
bitmap_size = bpl * (size_t) height;
bitmap = (unsigned char **) checkedmalloc(sizeof(unsigned char *) *
encode_planes);
for (i = 0; i < encode_planes; i++)
bitmap[i] = (unsigned char *) checkedmalloc(bitmap_size);
switch (type) {
case '1':
/* PBM text format */
p = bitmap[0];
for (y = 0; y < height; y++)
for (x = 0; x <= ((width-1) | 7); x++) {
*p <<= 1;
if (x < width)
*p |= getint(fin) & 1;
if ((x & 7) == 7)
++p;
}
break;
case '4':
/* PBM raw binary format */
fread(bitmap[0], bitmap_size, 1, fin);
break;
case '2':
case '5':
/* PGM */
if ((ULONG_MAX / bpp) / height < width) {
fprintf(stderr, "Image too large!\n");
exit(1);
}
image = (unsigned char *) checkedmalloc(width * height * bpp);
if (type == '2') {
for (x = 0; x < width * height; x++) {
v = getint(fin);
for (j = 0; j < bpp; j++)
image[x * bpp + (bpp - 1) - j] = v >> (j * 8);
}
} else
fread(image, width * height, bpp, fin);
jbg_split_planes(width, height, planes, encode_planes, image, bitmap,
use_graycode);
free(image);
break;
default:
fprintf(stderr, "Unsupported PBM type P%c!\n", type);
exit(1);
}
if (ferror(fin)) {
fprintf(stderr, "Problem while reading input file '%s", fnin);
perror("'");
exit(1);
}
if (feof(fin)) {
fprintf(stderr, "Unexpected end of input file '%s'!\n", fnin);
exit(1);
}
/* Test for valid parameters */
if (width < 1 || height < 1) {
fprintf(stderr, "Image dimensions must be positive!\n");
exit(1);
}
if (encode_planes < 1 || encode_planes > 255) {
fprintf(stderr, "Number of planes must be in range 1-255!\n");
exit(1);
}
/* Test the final byte in each image line for correct zero padding */
if ((width & 7) && type == '4') {
for (y = 0; y < height; y++)
if (bitmap[0][y * bpl + bpl - 1] & ((1 << (8 - (width & 7))) - 1)) {
fprintf(stderr, "Warning: No zero padding in last byte (0x%02x) of "
"line %lu!\n", bitmap[0][y * bpl + bpl - 1], y + 1);
break;
}
}
/* Apply JBIG algorithm and write BIE to output file */
/* initialize parameter struct for JBIG encoder*/
jbg_enc_init(&s, width, height, encode_planes, bitmap, data_out, fout);
/* Select number of resolution layers either directly or based
* on a given maximum size for the lowest resolution layer */
if (d >= 0)
jbg_enc_layers(&s, d);
else
jbg_enc_lrlmax(&s, mwidth, mheight);
/* Specify a few other options (each is ignored if negative) */
if (delay_at)
options |= JBG_DELAY_AT;
if (reset)
options |= JBG_SDRST;
if (comment) {
s.comment_len = strlen(comment);
s.comment = (unsigned char *) comment;
}
if (y1)
s.yd1 = y1;
jbg_enc_lrange(&s, dl, dh);
jbg_enc_options(&s, order, options, l0, mx, -1);
/* now encode everything and send it to data_out() */
jbg_enc_out(&s);
/* give encoder a chance to free its temporary data structures */
jbg_enc_free(&s);
/* check for file errors and close fout */
if (ferror(fout) || fclose(fout)) {
fprintf(stderr, "Problem while writing output file '%s", fnout);
perror("'");
exit(1);
}
/* In case the user wants to know all the gory details ... */
if (verbose) {
fprintf(stderr, "Information about the created JBIG bi-level image entity "
"(BIE):\n\n");
fprintf(stderr, " input image size: %lu x %lu pixel\n",
s.xd, s.yd);
fprintf(stderr, " bit planes: %d\n", s.planes);
if (s.planes > 1)
fprintf(stderr, " encoding: %s code, MSB first\n",
use_graycode ? "Gray" : "binary");
fprintf(stderr, " stripes: %lu\n", s.stripes);
fprintf(stderr, " lines per stripe in layer 0: %lu\n", s.l0);
fprintf(stderr, " total number of diff. layers: %d\n", s.d);
fprintf(stderr, " lowest layer in BIE: %d\n", s.dl);
fprintf(stderr, " highest layer in BIE: %d\n", s.dh);
fprintf(stderr, " lowest layer size: %lu x %lu pixel\n",
jbg_ceil_half(s.xd, s.d - s.dl), jbg_ceil_half(s.yd, s.d - s.dl));
fprintf(stderr, " highest layer size: %lu x %lu pixel\n",
jbg_ceil_half(s.xd, s.d - s.dh), jbg_ceil_half(s.yd, s.d - s.dh));
fprintf(stderr, " option bits:%s%s%s%s%s%s%s\n",
s.options & JBG_LRLTWO ? " LRLTWO" : "",
s.options & JBG_VLENGTH ? " VLENGTH" : "",
s.options & JBG_TPDON ? " TPDON" : "",
s.options & JBG_TPBON ? " TPBON" : "",
s.options & JBG_DPON ? " DPON" : "",
s.options & JBG_DPPRIV ? " DPPRIV" : "",
s.options & JBG_DPLAST ? " DPLAST" : "");
fprintf(stderr, " order bits:%s%s%s%s\n",
s.order & JBG_HITOLO ? " HITOLO" : "",
s.order & JBG_SEQ ? " SEQ" : "",
s.order & JBG_ILEAVE ? " ILEAVE" : "",
s.order & JBG_SMID ? " SMID" : "");
fprintf(stderr, " AT maximum x-offset: %d\n"
" AT maximum y-offset: %d\n", s.mx, s.my);
fprintf(stderr, " length of output file: %lu byte\n\n",
total_length);
}
return 0;
}

View File

@@ -0,0 +1,280 @@
/*
* pbmtojbg85 - Portable Bitmap to JBIG converter (T.85 version)
*
* Markus Kuhn - http://www.cl.cam.ac.uk/~mgk25/jbigkit/
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include "jbig85.h"
char *progname; /* global pointer to argv[0] */
/*
* Print usage message and abort
*/
static void usage(void)
{
fprintf(stderr,
"PBMtoJBIG converter " JBG85_VERSION " (T.85 version) --\n"
"creates bi-level image entity (BIE) as output file\n\n"
"usage: %s [<options>] [<input-file> | - [<output-file>]]\n\n"
"options:\n\n", progname);
fprintf(stderr,
" -s number\theight of a stripe\n");
fprintf(stderr,
" -m number\tmaximum adaptive template pixel horizontal offset (default 8)\n"
" -p number\toptions byte value: add TPBON=8, LRLTWO=64\n"
"\t\t(default 8 = TPBON)\n");
fprintf(stderr,
" -C string\tadd the provided string as a comment marker segment\n");
fprintf(stderr,
" -Y yi yr\tannounce in header initially the larger image height yi\n"
"\t\tand then announce after line yr has been encoded the real height\n"
"\t\tusing NEWLEN marker (for testing NEWLEN and VLENGTH=1 function)\n\n");
exit(1);
}
/*
* malloc() with exception handler
*/
void *checkedmalloc(size_t n)
{
void *p;
if ((p = malloc(n)) == NULL) {
fprintf(stderr, "Sorry, not enough memory available!\n");
exit(1);
}
return p;
}
/*
* Read an ASCII integer number from file f and skip any PBM
* comments which are encountered.
*/
static unsigned long getint(FILE *f)
{
int c;
unsigned long i;
while ((c = getc(f)) != EOF)
if (c == '#')
while ((c = getc(f)) != EOF && !(c == 13 || c == 10)) ;
else if (!isspace(c))
break;
if (c == EOF) return 0;
ungetc(c, f);
if (fscanf(f, "%lu", &i) != 1) {
fprintf(stderr, "Unsigned integer value expected!\n");
exit(1);
}
return i;
}
/*
* Callback procedure which is used by JBIG encoder to deliver the
* encoded data. It simply sends the bytes to the output file.
*/
static void data_out(unsigned char *start, size_t len, void *file)
{
fwrite(start, len, 1, (FILE *) file);
return;
}
int main (int argc, char **argv)
{
FILE *fin = stdin, *fout = stdout;
const char *fnin = NULL, *fnout = NULL;
int i, j, c;
int all_args = 0, files = 0;
unsigned long x, y;
unsigned long width, height;
size_t bpl;
char type;
unsigned char *p, *lines, *next_line;
unsigned char *prev_line = NULL, *prevprev_line = NULL;
struct jbg85_enc_state s;
int mx = -1;
unsigned long l0 = 0, yi = 0, yr = 0;
char *comment = NULL;
int options = JBG_TPBON;
/* parse command line arguments */
progname = argv[0];
for (i = 1; i < argc; i++) {
if (!all_args && argv[i][0] == '-')
if (argv[i][1] == 0) {
if (files++) usage();
} else
for (j = 1; j > 0 && argv[i][j]; j++)
switch(argv[i][j]) {
case '-' :
all_args = 1;
break;
case 0 :
if (files++) usage();
break;
case 'Y':
if (i+2 >= argc) usage();
j = -1;
yi = atol(argv[++i]);
yr = atol(argv[++i]);
break;
case 'p':
if (++i >= argc) usage();
j = -1;
options = atoi(argv[i]);
break;
case 's':
if (++i >= argc) usage();
j = -1;
l0 = atol(argv[i]);
break;
case 'm':
if (++i >= argc) usage();
j = -1;
mx = atoi(argv[i]);
break;
case 'C':
if (++i >= argc) usage();
j = -1;
comment = argv[i];
break;
default:
usage();
}
else
switch (files++) {
case 0: fnin = argv[i]; break;
case 1: fnout = argv[i]; break;
default:
usage();
}
}
/* open input file */
if (fnin) {
fin = fopen(fnin, "rb");
if (!fin) {
fprintf(stderr, "Can't open input file '%s", fnin);
perror("'");
exit(1);
}
} else
fnin = "<stdin>";
/* read PBM header */
while ((c = getc(fin)) != EOF && (isspace(c) || c == '#'))
if (c == '#')
while ((c = getc(fin)) != EOF && !(c == 13 || c == 10)) ;
type = getc(fin);
if (c != 'P' || (type != '1' && type != '4')) {
fprintf(stderr, "Input file '%s' does not look like a PBM file!\n", fnin);
exit(1);
}
width = getint(fin);
height = getint(fin);
fgetc(fin); /* skip line feed */
/* Test for valid parameters */
if (width < 1 || height < 1 ||
width > 0xffffffff || height > 0xffffffff) {
fprintf(stderr, "Invalid width or height!\n");
exit(1);
}
/* allocate buffer for a single image line */
bpl = (width >> 3) + !!(width & 7); /* bytes per line */
lines = (unsigned char *) checkedmalloc(bpl * 3);
/* open output file */
if (fnout) {
fout = fopen(fnout, "wb");
if (!fout) {
fprintf(stderr, "Can't open input file '%s", fnout);
perror("'");
exit(1);
}
} else
fnout = "<stdout>";
/* initialize parameter struct for JBIG encoder*/
jbg85_enc_init(&s, width, yi ? yi : height, data_out, fout);
/* Specify a few other options (each is ignored if negative) */
if (yi)
options |= JBG_VLENGTH;
if (comment) {
s.comment_len = strlen(comment);
s.comment = (unsigned char *) comment;
}
jbg85_enc_options(&s, options, l0, mx);
for (y = 0; y < height; y++) {
/* Use a 3-line ring buffer, because the encoder requires that the two
* previously supplied lines are still in memory when the next line is
* processed. */
next_line = lines + (y%3)*bpl;
switch (type) {
case '1':
/* PBM text format */
p = next_line;
for (x = 0; x <= ((width-1) | 7); x++) {
*p <<= 1;
if (x < width)
*p |= getint(fin) & 1;
if ((x & 7) == 7)
++p;
}
break;
case '4':
/* PBM raw binary format */
fread(next_line, bpl, 1, fin);
break;
default:
fprintf(stderr, "Unsupported PBM type P%c!\n", type);
exit(1);
}
if (ferror(fin)) {
fprintf(stderr, "Problem while reading input file '%s", fnin);
perror("'");
exit(1);
}
if (feof(fin)) {
fprintf(stderr, "Unexpected end of input file '%s'!\n", fnin);
exit(1);
}
/* JBIG compress another line and write out result via callback */
jbg85_enc_lineout(&s, next_line, prev_line, prevprev_line);
prevprev_line = prev_line;
prev_line = next_line;
/* adjust final image height via NEWLEN */
if (yi && y == yr)
jbg85_enc_newlen(&s, height);
}
/* check for file errors and close fout */
if (ferror(fout) || fclose(fout)) {
fprintf(stderr, "Problem while writing output file '%s", fnout);
perror("'");
exit(1);
}
return 0;
}

View File

@@ -0,0 +1,90 @@
.TH pgm 5 "12 November 1991"
.SH NAME
pgm - portable graymap file format
.SH DESCRIPTION
The portable graymap format is a lowest common denominator grayscale
file format.
.IX "PGM file format"
The definition is as follows:
.IP - 2
A "magic number" for identifying the file type.
A pgm file's magic number is the two characters "P2".
.IX "magic numbers"
.IP - 2
Whitespace (blanks, TABs, CRs, LFs).
.IP - 2
A width, formatted as ASCII characters in decimal.
.IP - 2
Whitespace.
.IP - 2
A height, again in ASCII decimal.
.IP - 2
Whitespace.
.IP - 2
The maximum gray value, again in ASCII decimal.
.IP - 2
Whitespace.
.IP - 2
Width * height gray values, each in ASCII decimal, between 0 and the specified
maximum value, separated by whitespace, starting at the top-left
corner of the graymap, proceeding in normal English reading order.
A value of 0 means black, and the maximum value means white.
.IP - 2
Characters from a "#" to the next end-of-line are ignored (comments).
.IP - 2
No line should be longer than 70 characters.
.PP
Here is an example of a small graymap in this format:
.nf
P2
# feep.pgm
24 7
15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0
0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
.fi
.PP
Programs that read this format should be as lenient as possible,
accepting anything that looks remotely like a graymap.
.PP
There is also a variant on the format, available
by setting the RAWBITS option at compile time. This variant is
different in the following ways:
.IX RAWBITS
.IP - 2
The "magic number" is "P5" instead of "P2".
.IP - 2
The gray values are stored as plain bytes, instead of ASCII decimal.
.IP - 2
No whitespace is allowed in the grays section, and only a single character
of whitespace (typically a newline) is allowed after the maxval.
.IP - 2
The files are smaller and many times faster to read and write.
.PP
Note that this raw format can only be used for maxvals less than
or equal to 255.
If you use the
.I pgm
library and try to write a file with a larger maxval,
it will automatically fall back on the slower but more general plain
format.
.SH "SEE ALSO"
fitstopgm(1), fstopgm(1), hipstopgm(1), lispmtopgm(1), psidtopgm(1),
rawtopgm(1),
pgmbentley(1), pgmcrater(1), pgmedge(1), pgmenhance(1), pgmhist(1), pgmnorm(1),
pgmoil(1), pgmramp(1), pgmtexture(1),
pgmtofits(1), pgmtofs(1), pgmtolispm(1), pgmtopbm(1),
pnm(5), pbm(5), ppm(5)
.SH AUTHOR
Copyright (C) 1989, 1991 by Jef Poskanzer.
.\" Permission to use, copy, modify, and distribute this software and its
.\" documentation for any purpose and without fee is hereby granted, provided
.\" that the above copyright notice appear in all copies and that both that
.\" copyright notice and this permission notice appear in supporting
.\" documentation. This software is provided "as is" without express or
.\" implied warranty.