From 0e3158930ee68a1a92b114c9e9ace41445b214f4 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sun, 19 Sep 2010 22:12:31 +0000 Subject: Add Pamtopdbimg git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1291 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- converter/other/Makefile | 9 +- converter/other/ipdb.c | 1386 +++++++++++++++++++++++++++++++++++++++++ converter/other/ipdb.h | 227 +++++++ converter/other/pamtopdbimg.c | 269 ++++++++ doc/HISTORY | 13 +- 5 files changed, 1896 insertions(+), 8 deletions(-) create mode 100644 converter/other/ipdb.c create mode 100644 converter/other/ipdb.h create mode 100644 converter/other/pamtopdbimg.c diff --git a/converter/other/Makefile b/converter/other/Makefile index 954f96d2..64c69b1c 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -93,7 +93,7 @@ ifneq ($(DONT_HAVE_PROCESS_MGMT),Y) PORTBINARIES += pstopnm endif -BINARIES = $(PORTBINARIES) pnmtorast rasttopnm +BINARIES = $(PORTBINARIES) pnmtorast rasttopnm pamtopdbimg ifeq ($(HAVE_PNGLIB),Y) BINARIES += pnmtopng pngtopam pamrgbatopng @@ -117,7 +117,7 @@ endif MERGEBINARIES = $(BINARIES) -EXTRA_OBJECTS = exif.o rast.o bmepsoe.o +EXTRA_OBJECTS = exif.o rast.o bmepsoe.o ipdb.o ifeq ($(HAVE_PNGLIB),Y) EXTRA_OBJECTS += pngtxt.o EXTRA_OBJECTS += pngx.o @@ -210,6 +210,11 @@ pnmtorast rasttopnm: %: %.o rast.o $(NETPBMLIB) $(LIBOPT) $(shell $(LIBOPT) $(NETPBMLIB)) \ $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) +pamtopdbimg: %: %.o ipdb.o $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $@.o ipdb.o \ + $(shell $(LIBOPT) $(NETPBMLIB)) \ + $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) + bmptopnm.o bmptopnm.o2: bmp.h pamtotga.o pamtotga.o2: tga.h diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c new file mode 100644 index 00000000..39e65b35 --- /dev/null +++ b/converter/other/ipdb.c @@ -0,0 +1,1386 @@ +/* + * + * Copyright (C) 1997 Eric A. Howe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Eric A. Howe (mu@trends.net) + * Bryan Henderson, 2010 + */ +#include +#include + +#include "mallocvar.h" +#include "nstring.h" +#include "ipdb.h" + +typedef uint32_t pilot_time_t; + +/* + * Pixel setting macros. + */ +#define setg16pixel(b,v,o) ((b) |= ((v) << (4 - 4*(o)))) +#define getg16pixel(b,o) (((b) >> (4 - 4*(o))) & 0x0f) +#define setgpixel(b,v,o) ((b) |= ((v) << (6 - 2*(o)))) +#define getgpixel(b,o) (((b) >> (6 - 2*(o))) & 0x03) +#define setmpixel(b,v,o) ((b) |= ((v) << (7 - (o)))) +#define getmpixel(b,o) (((b) >> (7 - (o))) & 0x01) + +/* + * Pixels/byte. + */ +#define img_ppb(i) ( \ + (i)->type == IMG_GRAY ? 4 : \ + (i)->type == IMG_GRAY16 ? 2 : \ + 8 \ + ) + +/* + * Size (in bytes) of an image's data. + */ +#define img_size(i) (size_t)((i)->width/img_ppb(i)*(i)->height) + +/* + * Return the start of row `r'. + */ +#define img_row(i, r) (&(i)->data[(r)*(i)->width/img_ppb(i)]) + +/* + * Only use four bytes of these. + */ +#define IPDB_vIMG "vIMG" +#define IPDB_View "View" + +/* + * Only use three bytes of this. + */ +#define IPDB_MYST "\x40\x6f\x80" + +static pilot_time_t const unixepoch = (66*365+17)*24*3600; + /* The unix epoch in Mac time (the Mac epoch is 00:00 UTC 1904.01.01). + The 17 is the number of leap years. + */ + +static const char * const errorDesc[] = { + /* E_BADCOLORS */ + "Invalid palette, only {0x00, 0x55, 0xAA, 0xFF} allowed.", + + /* E_NOTIMAGE */ + "Not an image file.", + + /* E_IMAGETHERE */ + "Image record already present, logic error.", + + /* E_IMAGENOTTHERE */ + "Image record required before text record, logic error.", + + /* E_TEXTTHERE */ + "Text record already present, logic error.", + + /* E_NOTRECHDR */ + "Invalid record header encountered.", + + /* E_UNKNOWNRECHDR */ + "Unknown record header.", + + /* E_TOOBIGG */ + "Image too big, maximum size approx. 640*400 gray pixels.", + + /* E_TOOBIGM */ + "Image too big, maximum size approx. 640*800 monochrome pixels.", +}; + + + +const char * +ipdb_err(int const e) { + + if (e < 0) + return e >= E_LAST ? errorDesc[-e - 1] : "unknown error"; + else + return strerror(e); +} + + + +static void +rechdr_free(RECHDR * const recP) { + + if (recP) { + free(recP->extra); + free(recP); + } +} + + + +static void +image_free(IMAGE * const imgP) { + + if (imgP) { + rechdr_free(imgP->r); + free(imgP->data); + free(imgP); + } +} + + + +static void +text_free(TEXT * const textP) { + + if (textP) { + rechdr_free(textP->r); + free(textP->data); + free(textP); + } +} + + + +static void +pdbhead_free(PDBHEAD * const headP) { + + free(headP); +} + + + +static void +ipdb_clear(IPDB * const pdbP) { + + if (pdbP) { + image_free(pdbP->i); + text_free(pdbP->t); + pdbhead_free(pdbP->p); + } +} + + + +void +ipdb_free(IPDB * const pdbP) { + + ipdb_clear(pdbP); + free(pdbP); +} + + + +static PDBHEAD * +pdbhead_alloc(const char * const name) { + + PDBHEAD * pdbHeadP; + + MALLOCVAR(pdbHeadP); + + if (pdbHeadP) { + MEMSZERO(pdbHeadP); + + STRSCPY(pdbHeadP->name, name == NULL ? "unnamed" : name); + + /* + * All of the Image Viewer pdb files that I've come across have + * 3510939142U (1997.08.16 14:38:22 UTC) here. I don't know where + * this bizarre date comes from but the real date works fine so + * I'm using it. + */ + pdbHeadP->ctime = + pdbHeadP->mtime = (pilot_time_t)time(NULL) + unixepoch; + + MEMSCPY(&pdbHeadP->type, IPDB_vIMG); + MEMSCPY(&pdbHeadP->id, IPDB_View); + } + return pdbHeadP; +} + + + +static RECHDR * +rechdr_alloc(int const type, + uint32_t const offset) { + + /* + * We never produce the `extra' bytes (we only read them from a file) + * so there is no point allocating them here. + */ + + RECHDR * recHdrP; + + MALLOCVAR(recHdrP); + + if (recHdrP) { + MEMSSET(recHdrP, 0); + + recHdrP->offset = offset; + recHdrP->rec_type = (uint8_t)(0xff & type); + MEMSCPY(&recHdrP->unknown, IPDB_MYST); + } + return recHdrP; +} + + + +/* + * The offset will be patched up as needed elsewhere. + */ +#define IMGOFFSET (PDBHEAD_SIZE + 8) + + + +static IMAGE * +image_alloc(const char * const name, + int const type, + int const w, + int const h) { + + bool failed; + IMAGE * imgP; + + failed = false; + + MALLOCVAR(imgP); + + if (imgP) { + MEMSZERO(imgP); + + STRSCPY(imgP->name, name == NULL ? "unnamed" : name); + imgP->type = type; + imgP->x_anchor = 0xffff; + imgP->y_anchor = 0xffff; + imgP->width = w; + imgP->height = h; + + imgP->r = rechdr_alloc(IMG_REC, IMGOFFSET); + + if (imgP->r) { + if (w != 0 && h != 0) { + MALLOCARRAY(imgP->data, w * h); + + if (imgP->data) { + MEMSZERO(imgP->data); + } else + failed = true; + } + if (failed) + rechdr_free(imgP->r); + } else + failed = true; + + if (failed) + image_free(imgP); + } else + failed = true; + + return failed ? NULL : imgP; +} + + + +static TEXT * +text_alloc(const char * const content) { + + TEXT * textP; + bool failed; + + failed = false; + /* + * The offset will be patched up later on when we know what it + * should be. + */ + + MALLOCVAR(textP); + + if (textP) { + MEMSZERO(textP); + + textP->r = rechdr_alloc(TEXT_REC, 0); + + if (textP->r) { + if (content) { + textP->data = strdup(content); + + if (!textP->data) + failed = true; + } + if (failed) + rechdr_free(textP->r); + } else + failed = true; + + if (failed) + text_free(textP); + } else + failed = true; + + return failed ? NULL : textP; +} + + + +IPDB * +ipdb_alloc(const char * const name) { + + IPDB * pdbP; + bool failed; + + failed = false; + + MALLOCVAR(pdbP); + + if (pdbP) { + MEMSZERO(pdbP); + + if (name) { + pdbP->p = pdbhead_alloc(name); + + if (!pdbP->p) + failed = true; + } + if (failed) + ipdb_free(pdbP); + } else + failed = true; + + return failed ? NULL : pdbP; +} + + + +static uint8_t * +decompress(const uint8_t * const buffer, + int const byteCount) { +/* + * This will *always* free `buffer'. + * + * The compression scheme used is a simple RLE; the control codes, + * CODE, are one byte and have the following meanings: + * + * CODE > 0x80 Insert (CODE + 1 - 0x80) copies of the next byte. + * CODE <= 0x80 Insert the next (CODE + 1) literal bytes. + * + * Compressed pieces can (and do) cross row boundaries. + */ + uint8_t * data; + + MALLOCARRAY(data, byteCount); + + if (data) { + const uint8_t * inP; + uint8_t * outP; + int bytesLeft; + + for (bytesLeft = byteCount, inP = &buffer[0], outP = &data[0]; + bytesLeft > 0; + ) { + + int got, put; + + if (*inP > 0x80) { + put = *inP++ + 1 - 0x80; + memset(outP, *inP, put); + got = 1; + } else { + put = *inP++ + 1; + memcpy(outP, inP, put); + got = put; + } + inP += got; + outP += put; + bytesLeft -= put; + } + } + free((void *)buffer); + return data; +} + + + +#define UNKNOWN_OFFSET (uint32_t)-1 + +static int +image_read_data(IMAGE * const imgP, + uint32_t const end_offset, + FILE * const fP) { + + int retval; + size_t data_size; + uint8_t * buffer; + + data_size = 0; /* initial value */ + + if (end_offset == UNKNOWN_OFFSET) { + /* + * Read until EOF. Some of them have an extra zero byte + * dangling off the end. I originally thought this was + * an empty note record (even though there was no record + * header for it); however, the release notes for Image + * Compression Manager 1.1 on http://www.pilotgear.com + * note this extra byte as a bug in Image Compression + * Manager 1.0 which 1.1 fixes. We'll just blindly read + * this extra byte and ignore it by paying attention to + * the image dimensions. + */ + MALLOCARRAY(buffer, img_size(imgP)); + + if (buffer == NULL) + retval = ENOMEM; + else { + data_size = fread(buffer, 1, img_size(imgP), fP); + if (data_size <= 0) + retval = EIO; + else + retval = 0; + + if (retval != 0) + free(buffer); + } + } else { + /* + * Read to the indicated offset. + */ + data_size = end_offset - ftell(fP) + 1; + + MALLOCARRAY(buffer, data_size); + + if (buffer == NULL) + retval = ENOMEM; + else { + ssize_t rc; + rc = fread(buffer, 1, data_size, fP); + if (rc != data_size) + retval = EIO; + else + retval = 0; + + if (retval != 0) + free(buffer); + } + } + if (retval == 0) { + /* + * Compressed data can cross row boundaries so we decompress + * the data here to avoid messiness in the row access functions. + */ + if (data_size != img_size(imgP)) { + imgP->data = decompress(buffer, img_size(imgP)); + if (imgP->data == NULL) + retval = ENOMEM; + else + imgP->compressed = true; + } else { + imgP->compressed = false; + imgP->data = buffer; + } + + if (retval != 0) + free(buffer); + } + return retval; +} + + + +static int +image_read(IMAGE * const imgP, + uint32_t const end_offset, + FILE * const fP) { + + if (imgP) { + imgP->r->offset = (uint32_t)ftell(fP); + + fread(&imgP->name, 1, 32, fP); + pm_readcharu(fP, &imgP->version); + pm_readcharu(fP, &imgP->type); + fread(&imgP->reserved1, 1, 4, fP); + fread(&imgP->note, 1, 4, fP); + pm_readbigshortu(fP, &imgP->x_last); + pm_readbigshortu(fP, &imgP->y_last); + fread(&imgP->reserved2, 1, 4, fP); + pm_readbigshortu(fP, &imgP->x_anchor); + pm_readbigshortu(fP, &imgP->y_anchor); + pm_readbigshortu(fP, &imgP->width); + pm_readbigshortu(fP, &imgP->height); + + image_read_data(imgP, end_offset, fP); + } + return 0; +} + + + +static int +text_read(TEXT * const textP, + FILE * const fP) { + + int retval; + char * s; + char buf[128]; + int used, alloced, len; + + if (textP == NULL) + return 0; + + textP->r->offset = (uint32_t)ftell(fP); + + /* + * What a pain in the ass! Why the hell isn't there a length + * attached to the text record? I suppose the designer wasn't + * concerned about non-seekable (i.e. pipes) input streams. + * Perhaps I'm being a little harsh, the lack of a length probably + * isn't much of an issue on the Pilot. + */ + used = 0; + alloced = 0; + s = NULL; + retval = 0; /* initial value */ + while ((len = fread(buf, 1, sizeof(buf), fP)) != 0 && retval == 0) { + if (buf[len - 1] == '\0') + --len; + if (used + len > alloced) { + alloced += 2 * sizeof(buf); + REALLOCARRAY(s, alloced); + + if (s == NULL) + retval = ENOMEM; + } + if (retval == 0) { + memcpy(s + used, buf, len); + used += len; + } + } + if (retval == 0) { + textP->data = calloc(1, used + 1); + if (textP->data == NULL) + retval = ENOMEM; + else + memcpy(textP->data, s, used); + } + if (s) + free(s); + + return retval; +} + + + +static int +pdbhead_read(PDBHEAD * const pdbHeadP, + FILE * const fP) { + + int retval; + + fread(pdbHeadP->name, 1, 32, fP); + pm_readbigshortu(fP, &pdbHeadP->flags); + pm_readbigshortu(fP, &pdbHeadP->version); + pm_readbiglongu2(fP, &pdbHeadP->ctime); + pm_readbiglongu2(fP, &pdbHeadP->mtime); + pm_readbiglongu2(fP, &pdbHeadP->btime); + pm_readbiglongu2(fP, &pdbHeadP->mod_num); + pm_readbiglongu2(fP, &pdbHeadP->app_info); + pm_readbiglongu2(fP, &pdbHeadP->sort_info); + fread(pdbHeadP->type, 1, 4, fP); + fread(pdbHeadP->id, 1, 4, fP); + pm_readbiglongu2(fP, &pdbHeadP->uniq_seed); + pm_readbiglongu2(fP, &pdbHeadP->next_rec); + pm_readbigshortu(fP, &pdbHeadP->num_recs); + + if (!memeq(pdbHeadP->type, IPDB_vIMG, 4) + || !memeq(pdbHeadP->id, IPDB_View, 4)) + retval = E_NOTIMAGE; + else + retval = 0; + + return retval; +} + + + +static int +rechdr_read(RECHDR * const rechdrP, + FILE * const fP) { + + int retval; + off_t len; + + pm_readbiglongu2(fP, &rechdrP->offset); + + len = (off_t)rechdrP->offset - ftell(fP); + switch(len) { + case 4: + case 12: + /* + * Version zero (eight bytes of record header) or version + * two with a note (two chunks of eight record header bytes). + */ + fread(&rechdrP->unknown[0], 1, 3, fP); + fread(&rechdrP->rec_type, 1, 1, fP); + rechdrP->n_extra = 0; + rechdrP->extra = NULL; + retval = 0; + break; + case 6: + /* + * Version one (ten bytes of record header). + */ + fread(&rechdrP->unknown[0], 1, 3, fP); + fread(&rechdrP->rec_type, 1, 1, fP); + rechdrP->n_extra = 2; + MALLOCARRAY(rechdrP->extra, rechdrP->n_extra); + if (rechdrP->extra == NULL) + retval = ENOMEM; + else { + fread(rechdrP->extra, 1, rechdrP->n_extra, fP); + retval = 0; + } + break; + default: + /* + * hmmm.... I'll assume this is the record header + * for a text record. + */ + fread(&rechdrP->unknown[0], 1, 3, fP); + fread(&rechdrP->rec_type, 1, 1, fP); + rechdrP->n_extra = 0; + rechdrP->extra = NULL; + retval = 0; + break; + } + if (retval == 0) { + if ((rechdrP->rec_type != IMG_REC && rechdrP->rec_type != TEXT_REC) + || !memeq(rechdrP->unknown, IPDB_MYST, 3)) + retval = E_NOTRECHDR; + } + return retval; +} + + + +int +ipdb_read(IPDB * const pdbP, + FILE * const fP) { + + int retval; + + ipdb_clear(pdbP); + + pdbP->p = pdbhead_alloc(NULL); + + if (pdbP->p == NULL) + retval = ENOMEM; + else { + int status; + + status = pdbhead_read(pdbP->p, fP); + + if (status != 0) + retval = status; + else { + pdbP->i = image_alloc(pdbP->p->name, IMG_GRAY, 0, 0); + if (pdbP->i == NULL) + retval = ENOMEM; + else { + int status; + status = rechdr_read(pdbP->i->r, fP); + if (status != 0) + retval = status; + else { + if (pdbP->p->num_recs > 1) { + pdbP->t = text_alloc(NULL); + if (pdbP->t == NULL) + retval = ENOMEM; + else { + int status; + status = rechdr_read(pdbP->t->r, fP); + if (status != 0) + retval = status; + else + retval = 0; + } + } else + retval = 0; + + if (retval == 0) { + uint32_t const offset = + pdbP->t == NULL ? + UNKNOWN_OFFSET : pdbP->t->r->offset - 1; + + int status; + + status = image_read(pdbP->i, offset, fP); + if (status != 0) + retval = status; + else { + if (pdbP->t != NULL) { + int status; + + status = text_read(pdbP->t, fP); + if (status != 0) + retval = status; + } + } + } + } + } + } + } + return retval; +} + + + +static const uint8_t * +g16unpack(const uint8_t * const p, + uint8_t * const g, + int const w) { + + static const uint8_t pal[] = + {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; + const uint8_t * seg; + unsigned int i; + + for (i = 0, seg = p; i < w; i += 2, ++seg) { + g[i + 0] = pal[getg16pixel(*seg, 0)]; + g[i + 1] = pal[getg16pixel(*seg, 1)]; + } + return g; +} + + + +static const uint8_t * +gunpack(const uint8_t * const p, + uint8_t * const g, + int const w) { + + static const uint8_t pal[] = {0xff, 0xaa, 0x55, 0x00}; + const uint8_t * seg; + unsigned int i; + + for (i = 0, seg = p; i < w; i += 4, ++seg) { + g[i + 0] = pal[getgpixel(*seg, 0)]; + g[i + 1] = pal[getgpixel(*seg, 1)]; + g[i + 2] = pal[getgpixel(*seg, 2)]; + g[i + 3] = pal[getgpixel(*seg, 3)]; + } + return g; +} + + + +static const uint8_t * +munpack(const uint8_t * const p, + uint8_t * const b, + int const w) { + + static const uint8_t pal[] = {0x00, 0x01}; + const uint8_t * seg; + unsigned int i; + + for (i = 0, seg = p; i < w; i += 8, ++seg) { + b[i + 0] = pal[getmpixel(*seg, 0)]; + b[i + 1] = pal[getmpixel(*seg, 1)]; + b[i + 2] = pal[getmpixel(*seg, 2)]; + b[i + 3] = pal[getmpixel(*seg, 3)]; + b[i + 4] = pal[getmpixel(*seg, 4)]; + b[i + 5] = pal[getmpixel(*seg, 5)]; + b[i + 6] = pal[getmpixel(*seg, 6)]; + b[i + 7] = pal[getmpixel(*seg, 7)]; + } + return b; +} + + + +const uint8_t * +ipdb_g16row(IPDB * const pdbP, + unsigned int const row, + uint8_t * const buffer) { + + return g16unpack(img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); +} + + + +const uint8_t * +ipdb_grow(IPDB * const pdbP, + unsigned int const row, + uint8_t * const buffer) { + + return gunpack(img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); +} + + + +const uint8_t * +ipdb_mrow(IPDB * const pdbP, + unsigned int const row, + uint8_t * const buffer) { + + return munpack(img_row(pdbP->i, row), buffer, ipdb_width(pdbP)); +} + + + +int +ipdb_remove_image(IPDB * const pdbP) { + + int retval; + /* + * There's no point in fiddling with pdbP->t->r->offset here since we + * never know what it really should be until write-time anyway. + */ + + if (pdbP->i == NULL) + retval = 0; + else { + image_free(pdbP->i); + --pdbP->p->num_recs; + retval = 0; + } + return retval; +} + + + +int +ipdb_remove_text(IPDB * const pdbP) { + + int retval; + + if (pdbP->t == NULL) + retval = 0; + else { + text_free(pdbP->t); + if (pdbP->i) + pdbP->i->r->offset -= 8; + --pdbP->p->num_recs; + retval = 0; + } + return retval; +} + + + +static int +pdbhead_write(PDBHEAD * const pdbheadP, + FILE * const fP) { + + fwrite(pdbheadP->name, 1, 32, fP); + pm_writebigshort(fP, pdbheadP->flags); + pm_writebigshort(fP, pdbheadP->version); + pm_writebiglong(fP, pdbheadP->ctime); + pm_writebiglong(fP, pdbheadP->mtime); + pm_writebiglong(fP, pdbheadP->btime); + pm_writebiglong(fP, pdbheadP->mod_num); + pm_writebiglong(fP, pdbheadP->app_info); + pm_writebiglong(fP, pdbheadP->sort_info); + fwrite(pdbheadP->type, 1, 4, fP); + fwrite(pdbheadP->id, 1, 4, fP); + pm_writebiglong(fP, pdbheadP->uniq_seed); + pm_writebiglong(fP, pdbheadP->next_rec); + pm_writebigshort(fP, pdbheadP->num_recs); + + return 0; +} + + + +static int +rechdr_write(RECHDR * const rechdrP, + FILE * const fP) { + + if (rechdrP) { + pm_writebiglong(fP, rechdrP->offset); + fwrite(rechdrP->unknown, 1, 3, fP); + fwrite(&rechdrP->rec_type, 1, 1, fP); + + if (rechdrP->n_extra != 0) + fwrite(rechdrP->extra, 1, rechdrP->n_extra, fP); + } + return 0; +} + + + +static int +image_write(IMAGE * const imgP, + uint8_t * const data, + size_t const n, + FILE * const fP) { + + fwrite(imgP->name, 1, 32, fP); + fwrite(&imgP->version, 1, 1, fP); + fwrite(&imgP->type, 1, 1, fP); + fwrite(imgP->reserved1, 1, 4, fP); + fwrite(imgP->note, 1, 4, fP); + pm_writebigshort(fP, imgP->x_last); + pm_writebigshort(fP, imgP->y_last); + fwrite(imgP->reserved2, 1, 2, fP); + pm_writebigshort(fP, imgP->x_anchor); + pm_writebigshort(fP, imgP->y_anchor); + pm_writebigshort(fP, imgP->width); + pm_writebigshort(fP, imgP->height); + fwrite(data, 1, n, fP); + + return 0; +} + + + +static int +text_write(TEXT * const textP, + FILE * const fP) { + + if (textP) + fwrite(textP->data, 1, strlen(textP->data), fP); + + return 0; +} + + + +typedef struct { + unsigned int match; + uint8_t buf[128]; + int mode; + size_t len; + size_t used; + uint8_t * p; +} RLE; +#define MODE_MATCH 0 +#define MODE_LIT 1 +#define MODE_NONE 2 + +#define reset(r) { \ + (r)->match = 0xffff; \ + (r)->mode = MODE_NONE; \ + (r)->len = 0; \ + } + + + +static void +put_match(RLE * const rleP, + size_t const n) { + + *rleP->p++ = 0x80 + n - 1; + *rleP->p++ = rleP->match; + rleP->used += 2; + reset(rleP); +} + + + +static void +put_lit(RLE * const rleP, + size_t const n) { + + *rleP->p++ = n - 1; + rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n; + rleP->used += n + 1; + reset(rleP); +} + + + +static size_t +compress(const uint8_t * const inData, + size_t const n_in, + uint8_t * const out) { + + static void (*put[])(RLE *, size_t) = {put_match, put_lit}; + RLE rle; + size_t i; + const uint8_t * p; + + MEMSZERO(&rle); + rle.p = out; + reset(&rle); + + for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) { + if (*p == rle.match) { + if (rle.mode == MODE_LIT && rle.len > 1) { + put_lit(&rle, rle.len - 1); + ++rle.len; + rle.match = *p; + } + rle.mode = MODE_MATCH; + ++rle.len; + } else { + if (rle.mode == MODE_MATCH) + put_match(&rle, rle.len); + rle.mode = MODE_LIT; + rle.match = *p; + rle.buf[rle.len++] = *p; + } + if (rle.len == 128) + put[rle.mode](&rle, rle.len); + } + if (rle.len != 0) + put[rle.mode](&rle, rle.len); + + return rle.used; +} + + + +int +ipdb_write(IPDB * const pdbP, + int const comp, + FILE * const fP) { + + int retval; + + if (pdbP->i == NULL) + retval = E_IMAGENOTTHERE; + else { + RECHDR * const trP = pdbP->t == NULL ? NULL : pdbP->t->r; + RECHDR * const irP= pdbP->i->r; + + int n; + uint8_t * data; + + n = img_size(pdbP->i); /* initial value */ + data = pdbP->i->data; /* initial value */ + + if (comp == IPDB_NOCOMPRESS) + retval = 0; + else { + /* Allocate for the worst case. */ + int const allocSz = (3*n + 2)/2; + + MALLOCARRAY(data, allocSz); + + if (data == NULL) + retval = ENOMEM; + else { + int sz; + sz = compress(pdbP->i->data, n, data); + if (comp == IPDB_COMPMAYBE && sz >= n) { + free(data); + data = pdbP->i->data; + } else { + pdbP->i->compressed = TRUE; + if (pdbP->i->type == IMG_GRAY16) + pdbP->i->version = 9; + else + pdbP->i->version = 1; + if (pdbP->t != NULL) + pdbP->t->r->offset -= n - sz; + n = sz; + } + retval = 0; + } + + if (retval == 0) { + retval = pdbhead_write(pdbP->p, fP); + if (retval == 0) { + retval = rechdr_write(irP, fP); + if (retval == 0) { + retval = rechdr_write(trP, fP); + if (retval == 0) { + retval = image_write(pdbP->i, data, n, fP); + if (retval == 0) { + retval = text_write(pdbP->t, fP); + } + } + } + } + if (data != pdbP->i->data) + free(data); + } + } + } + return retval; +} + + + +static int +g16pack(const uint8_t * const inData, + uint8_t * const p, + int const w) { + + int off, i; + uint8_t * seg; + const uint8_t * g; + + for(i = off = 0, seg = p, g=inData; i < w; ++i, ++g) { + switch(*g) { + case 0xff: setg16pixel(*seg, 0x00, off); break; + case 0xee: setg16pixel(*seg, 0x01, off); break; + case 0xdd: setg16pixel(*seg, 0x02, off); break; + case 0xcc: setg16pixel(*seg, 0x03, off); break; + case 0xbb: setg16pixel(*seg, 0x04, off); break; + case 0xaa: setg16pixel(*seg, 0x05, off); break; + case 0x99: setg16pixel(*seg, 0x06, off); break; + case 0x88: setg16pixel(*seg, 0x07, off); break; + case 0x77: setg16pixel(*seg, 0x08, off); break; + case 0x66: setg16pixel(*seg, 0x09, off); break; + case 0x55: setg16pixel(*seg, 0x0a, off); break; + case 0x44: setg16pixel(*seg, 0x0b, off); break; + case 0x33: setg16pixel(*seg, 0x0c, off); break; + case 0x22: setg16pixel(*seg, 0x0d, off); break; + case 0x11: setg16pixel(*seg, 0x0e, off); break; + case 0x00: setg16pixel(*seg, 0x0f, off); break; + default: return E_BADCOLORS; + } + if (++off == 2) { + ++seg; + off = 0; + } + } + return w/2; +} + + + +static int +gpack(const uint8_t * const inData, + uint8_t * const p, + int const w) { + + int off, i; + uint8_t * seg; + const uint8_t * g; + + for (i = off = 0, seg = p, g = inData; i < w; ++i, ++g) { + switch(*g) { + case 0xff: setgpixel(*seg, 0x00, off); break; + case 0xaa: setgpixel(*seg, 0x01, off); break; + case 0x55: setgpixel(*seg, 0x02, off); break; + case 0x00: setgpixel(*seg, 0x03, off); break; + default: return E_BADCOLORS; + } + if (++off == 4) { + ++seg; + off = 0; + } + } + return w/4; +} + + + +static int +mpack(const uint8_t * const inData, + uint8_t * const p, + int const w) { + + int off, i; + uint8_t * seg; + const uint8_t * b; + + for (i = off = 0, seg = p, b = inData; i < w; ++i, ++b) { + setmpixel(*seg, *b == 0, off); + if (++off == 8) { + ++seg; + off = 0; + } + } + return w/8; +} + + + +static int +adjust_dims(unsigned int const w, + unsigned int const h, + unsigned int * const awP, + unsigned int * const ahP) { + + unsigned int provW, provH; + + provW = w; + provH = h; + if (provW % 16 != 0) + provW += 16 - (provW % 16); + if (provW < 160) + provW = 160; + if (provH < 160) + provH = 160; + + *awP = provW; + *ahP = provH; + + return w == provW && h == provH; +} + + + +/* + * You can allocate only 64k chunks of memory on the pilot and that + * supplies an image size limit. + */ +#define MAX_SIZE(t) ((1 << 16)*((t) == IMG_GRAY ? 4 : 8)) +#define MAX_ERROR(t) ((t) == IMG_GRAY ? E_TOOBIGG : E_TOOBIGM) + +static int +image_insert_init(IPDB * const pdbP, + int const uw, + int const uh, + int const type) { + + char * const name = pdbP->p->name; + unsigned int w, h; + int retval; + + if (pdbP->p->num_recs != 0) + retval = E_IMAGETHERE; + else { + adjust_dims(uw, uh, &w, &h); + if (w*h > MAX_SIZE(type)) + retval = MAX_ERROR(type); + else { + pdbP->i = image_alloc(name, type, w, h); + if(pdbP->i == NULL) + retval = ENOMEM; + else { + pdbP->p->num_recs = 1; + + retval =0; + } + } + } + return retval; +} + + + +int +ipdb_insert_g16image(IPDB * const pdbP, + int const w, + int const h, + const uint8_t * const gArg) { + + int retval; + int i; + + i = image_insert_init(pdbP, w, h, IMG_GRAY16); + if (i != 0) + retval = i; + else { + int const incr = ipdb_width(pdbP)/2; + unsigned int i; + uint8_t * p; + const uint8_t * g; + + for (i = 0, p = pdbP->i->data, g = gArg, retval = 0; + i < h && retval == 0; + ++i, p += incr, g += w) { + + int const len = g16pack(g, p, w); + if (len < 0) + retval = len; + } + } + return retval; +} + + + +int +ipdb_insert_gimage(IPDB * const pdbP, + int const w, + int const h, + const uint8_t * const gArg) { + + int i; + int retval; + + i = image_insert_init(pdbP, w, h, IMG_GRAY); + + if (i != 0) + retval = i; + else { + int const incr = ipdb_width(pdbP)/4; + unsigned int i; + uint8_t * p; + const uint8_t * g; + + for (i = 0, p = pdbP->i->data, g = gArg, retval = 0; + i < h && retval == 0; + ++i, p += incr, g += w) { + + int const len = gpack(g, p, w); + if (len < 0) + retval = len; + } + } + return retval; +} + + + +int +ipdb_insert_mimage(IPDB * const pdbP, + int const w, + int const h, + const uint8_t * const bArg) { + + int retval; + int i; + + i = image_insert_init(pdbP, w, h, IMG_MONO); + if (i != 0) + retval = i; + else { + int const incr = ipdb_width(pdbP)/8; + unsigned int i; + uint8_t * p; + const uint8_t * b; + + for (i = 0, p = pdbP->i->data, b = bArg, retval = 0; + i < h && retval == 0; + ++i, p += incr, b += w) { + + int const len = mpack(b, p, w); + if (len < 0) + retval = len; + } + } + return retval; +} + + + +int +ipdb_insert_text(IPDB * const pdbP, + const char * const s) { + + int retval; + + if (pdbP->i == NULL) + retval = E_IMAGENOTTHERE; + else if (pdbP->p->num_recs == 2) + retval = E_TEXTTHERE; + else { + pdbP->t = text_alloc(s); + if (pdbP->t == NULL) + retval = ENOMEM; + else { + pdbP->p->num_recs = 2; + + pdbP->i->r->offset += 8; + pdbP->t->r->offset = + pdbP->i->r->offset + IMAGESIZE + img_size(pdbP->i); + + retval = 0; + } + } + return retval; +} diff --git a/converter/other/ipdb.h b/converter/other/ipdb.h new file mode 100644 index 00000000..d00fde1a --- /dev/null +++ b/converter/other/ipdb.h @@ -0,0 +1,227 @@ +/* + * ipdb.h + * Image Viewer PDB file functions. + * + * Copyright (C) 1997 Eric A. Howe + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Eric A. Howe (mu@trends.net) + * Bryan Henderson 2010 + */ +#ifndef IPDB_H_INCLUDED +#define IPDB_H_INCLUDED + +#include +#include + +/* + * Extra error numbers, feed these (or errno values) to ipdb_err() + * to get strings. + */ +#define E_BADCOLORS -1 +#define E_NOTIMAGE -2 +#define E_IMAGETHERE -3 +#define E_IMAGENOTTHERE -4 +#define E_TEXTTHERE -5 +#define E_NOTRECHDR -6 +#define E_UNKNOWNRECHDR -7 +#define E_TOOBIGG -8 +#define E_TOOBIGM -9 +#define E_LAST -9 + +/* + * The standard pdb header. + */ +typedef struct { + char name[32]; /* nul terminated */ + uint16_t flags; /* 0 */ + uint16_t version; /* 0 */ + uint32_t ctime; /* mac time */ + uint32_t mtime; /* mac time */ + uint32_t btime; /* mac time */ + uint32_t mod_num; /* 0 */ + uint32_t app_info; /* 0 */ + uint32_t sort_info; /* 0 */ + uint8_t type[4]; /* vIMG */ + uint8_t id[4]; /* View */ + uint32_t uniq_seed; /* 0 */ + uint32_t next_rec; /* 0 */ + uint16_t num_recs; /* 1 */ +} PDBHEAD; +#define PDBHEAD_SIZE (32 + 2*2 + 6*4 + 4 + 4 + 2*4 + 2) + +/* + * Between the pdb header and the image header we find some "mystery" bytes, + * these are supposed to be eight byte record headers but sometimes there + * are ten bytes. Version zero files use eight bytes, version 1 files appear + * to use ten bytes, files with attached notes (version 2?) use two sets of + * eight bytes. Note that this version isn't the same as the `version' field + * in IMAGE, that version only indicates if the file is compressed or not. + * + * The first four bytes of each piece are a four byte offset to the start + * of the corresponding image header or text record; the next three bytes + * (40 6f 80) are some kind of magic (they are always the same); the next + * byte is zero for image records and 1 for text records; any remaining + * mystery bytes (zero or two) are always zero. + */ +typedef struct { + uint32_t offset; /* offset, from zero, to the image */ + uint8_t unknown[3]; /* 40 6f 80 */ + uint8_t rec_type; /* byte seven, TEXT_REC || IMG_REC */ + size_t n_extra; /* bytes in extra */ + uint8_t *extra; /* extra unknown end bytes */ +} RECHDR; +#define IMG_REC (uint8_t)(0x00) +#define TEXT_REC (uint8_t)(0x01) + +/* + * The image headers. + */ +typedef struct { + RECHDR * r; + + /* + * Whether the image was originally compressed. Since compressed + * data can cross row boundaries we have to uncompress the whole + * thing during reads so `data' is always in the uncompressed + * (but packed) format. I think we can just use the `version' + * field for this but a little extra paranoia is worth a couple + * of bytes. This is also set after a write to indicate if + * compression was used. + */ + int compressed; + + /* + * The actual image header, this starts at `m->offset'. + */ + char name[32]; /* nul terminated */ + uint8_t version; /* 0 => uncompressed, 1 => compressed */ + uint8_t type; /* GRAYSCALE || MONOCHROME */ + uint8_t reserved1[4]; /* zero */ + uint8_t note[4]; /* zero */ + uint16_t x_last; /* zero */ + uint16_t y_last; /* zero */ + uint8_t reserved2[4]; /* zero */ + uint16_t x_anchor; /* 0xffff */ + uint16_t y_anchor; /* 0xffff */ + uint16_t width; /* pixels (must be 0 mod 16) */ + uint16_t height; /* pixels */ + + /* + * And finally, the actual image data. We always store the + * image data as 4 pixels per byte uncompressed. Any compression + * or decompression is done at I/O time. + */ + uint8_t * data; +} IMAGE; +#define IMAGESIZE (32 + 1 + 1 + 4 + 4 + 2*2 + 4 + 2*2 + 2*2) + +/* + * Image types for IMAGE.type. + */ +#define IMG_GRAY16 ((uint8_t)2) +#define IMG_GRAY ((uint8_t)0) +#define IMG_MONO ((uint8_t)0xff) + +/* + * Compression constants for IMAGE.version. + */ +#define IMG_COMPRESSED ((uint8_t)0x01) +#define IMG_UNCOMPRESSED ((uint8_t)0x00) + +/* + * The notes record. If this exists, it will follow the image record. + */ +typedef struct { + RECHDR *r; + char *data; /* the actual text as a normal string */ +} TEXT; + +/* + * One PDB file. The `t' field will be NULL if there is no note. + */ +typedef struct { + PDBHEAD * p; + IMAGE * i; + TEXT * t; +} IPDB; + +/* + * Flags for ipdb_write(). + */ +#define IPDB_COMPMAYBE 0 /* compress if it does any good */ +#define IPDB_NOCOMPRESS (1 << 1) /* don't compress */ +#define IPDB_COMPRESS (1 << 2) /* compress */ + +#define ipdb_width(pdb) ((pdb)->i->width) +#define ipdb_height(pdb) ((pdb)->i->height) +#define ipdb_text(pdb) ((pdb)->t == NULL ? NULL : (pdb)->t->data) +#define ipdb_compressed(pdb) ((pdb)->i->compressed) +#define ipdb_ctime(pdb) ((time_t)((pdb)->p->ctime - UNIXEPOCH)) +#define ipdb_mtime(pdb) ((time_t)((pdb)->p->mtime - UNIXEPOCH)) +#define ipdb_btime(pdb) ((time_t)((pdb)->p->btime - UNIXEPOCH)) +#define ipdb_iname(pdb) ((pdb)->i->name) +#define ipdb_pname(pdb) ((pdb)->p->name) +#define ipdb_version(pdb) ((pdb)->i->version) +#define ipdb_type(pdb) ((pdb)->i->type) +#define ipdb_xlast(pdb) ((pdb)->i->x_last) +#define ipdb_ylast(pdb) ((pdb)->i->y_last) +#define ipdb_xanchor(pdb) ((pdb)->i->x_anchor) +#define ipdb_yanchor(pdb) ((pdb)->i->y_anchor) + +const char * +ipdb_err(int error); + +int +ipdb_read(IPDB *, FILE *); + +int +ipdb_write(IPDB *, int, FILE *); + +int +ipdb_insert_g16image(IPDB *, int, int, const uint8_t *); + +int +ipdb_insert_gimage(IPDB *, int, int, const uint8_t*); + +int +ipdb_insert_mimage(IPDB *, int, int, const uint8_t *); + +int +ipdb_insert_text(IPDB *, const char *); + +int +ipdb_remove_image(IPDB *); + +int +ipdb_remove_text(IPDB *); + +const uint8_t * +ipdb_g16row(IPDB *, unsigned int, uint8_t *); + +const uint8_t * +ipdb_grow(IPDB *, unsigned int, uint8_t *); + +const uint8_t * +ipdb_mrow(IPDB *, unsigned int, uint8_t *); + +void +ipdb_free(IPDB *); + +IPDB * +ipdb_alloc(const char *); + +#endif diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c new file mode 100644 index 00000000..58869d56 --- /dev/null +++ b/converter/other/pamtopdbimg.c @@ -0,0 +1,269 @@ +/*============================================================================= + pamtopdbimg +=============================================================================== + + Convert Netpbm image to Palm Pilot PDB Image format (for viewing by + Pilot Image Viewer). + + Bryan Henderson derived this from Eric Howe's programs named + 'pgmtoimgv' and 'pbmtoimgv'. +=============================================================================*/ +/* + * Copyright (C) 1997 Eric A. Howe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Eric A. Howe (mu@trends.net) + * Bryan Henderson, September 2010. + */ + +#include +#include +#include + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" +#include "pam.h" + +#include "ipdb.h" + +enum CompMode {COMPRESSED, MAYBE, UNCOMPRESSED}; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ + const char * title; + const char * notefile; /* NULL if not specified */ + enum CompMode compMode; + unsigned int depth4; +}; + + + +static void +parseCommandLine(int argc, const char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int titleSpec, notefileSpec; + unsigned int compressed, maybeCompressed, uncompressed; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "title", OPT_STRING, &cmdlineP->title, + &titleSpec, 0); + OPTENT3(0, "notefile", OPT_STRING, &cmdlineP->notefile, + ¬efileSpec, 0); + OPTENT3(0, "compressed", OPT_FLAG, NULL, + &compressed, 0); + OPTENT3(0, "maybecompressed", OPT_FLAG, NULL, + &maybeCompressed, 0); + OPTENT3(0, "uncompressed", OPT_FLAG, NULL, + &uncompressed, 0); + OPTENT3(0, "4depth", OPT_FLAG, NULL, + &cmdlineP->depth4, 0); + + opt.opt_table = option_def; + opt.short_allowed = false; /* We have no short (old-fashioned) options */ + opt.allowNegNum = false; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + + if (!titleSpec) + cmdlineP->title = "unnamed"; + + if (!notefileSpec) + cmdlineP->notefile = NULL; + + if (compressed + uncompressed + maybeCompressed > 1) + pm_error("You may specify only one of -compressed, -uncompressed, " + "-maybecompressed"); + if (compressed) + cmdlineP->compMode = COMPRESSED; + else if (uncompressed) + cmdlineP->compMode = UNCOMPRESSED; + else if (maybeCompressed) + cmdlineP->compMode = MAYBE; + else + cmdlineP->compMode = MAYBE; + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name"); +} + + + +static void +readimg(IPDB * const pdbP, + FILE * const ifP, + bool const depth4) { + + struct pam inpam; + tuple * tuplerow; + int status; + uint8_t * imgRaster; + unsigned int row; + + pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + + if (strneq(inpam.tuple_type, "RGB", 3)) + pm_error("Input image is color. Cannot make a Palm color image."); + + MALLOCARRAY(imgRaster, inpam.width * inpam.height); + + tuplerow = pnm_allocpamrow(&inpam); + + for (row = 0; row < inpam.height; ++row) { + unsigned int col; + + pnm_readpamrow(&inpam, tuplerow); + + for (col = 0; col < inpam.width; ++col) + imgRaster[row * inpam.height + col] = tuplerow[col][0]; + } + + if (inpam.maxval == 1) + status = ipdb_insert_mimage(pdbP, inpam.width, inpam.height, + imgRaster); + else if (depth4) + status = ipdb_insert_g16image(pdbP, inpam.width, inpam.height, + imgRaster); + else + status = ipdb_insert_gimage(pdbP, inpam.width, inpam.height, + imgRaster); + + if (status != 0) + pm_error("ipdb_insert failed. Error %d (%s)", + status, ipdb_err(status)); + + pnm_freepamrow(tuplerow); + free(imgRaster); +} + + + +static void +readtxt(IPDB * const pdbP, + const char * const noteFileName) { + + struct stat st; + char * fileContent; + FILE * fP; + int n; + int rc; + size_t bytesRead; + + rc = stat(noteFileName, &st); + + if (rc != 0) + pm_error("stat of '%s' failed, errno = %d (%s)", + noteFileName, errno, strerror(errno)); + + fP = pm_openr(noteFileName); + + MALLOCARRAY(fileContent, st.st_size + 1); + + if (fileContent == NULL) + pm_error("Couldn't get %lu bytes of storage to read in note file", + (unsigned long) st.st_size); + + bytesRead = fread(fileContent, 1, st.st_size, fP); + + if (bytesRead != st.st_size) + pm_error("Failed to read note file '%s'. Errno = %d (%s)", + noteFileName, errno, strerror(errno)); + + pm_close(fP); + + /* Chop of trailing newlines */ + for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n) + fileContent[n] = '\0'; + + ipdb_insert_text(pdbP, fileContent); +} + + + +int +main(int argc, const char **argv) { + + struct cmdlineInfo cmdline; + IPDB * pdbP; + FILE * ifP; + int comp; + int status; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + switch (cmdline.compMode) { + case COMPRESSED: comp = IPDB_COMPRESS; break; + case UNCOMPRESSED: comp = IPDB_NOCOMPRESS; break; + case MAYBE: comp = IPDB_COMPMAYBE; break; + } + + pdbP = ipdb_alloc(cmdline.title); + + if (pdbP == NULL) + pm_error("Failed to allocate IPDB structure"); + + readimg(pdbP, ifP, cmdline.depth4); + + if (cmdline.notefile) + readtxt(pdbP, cmdline.notefile); + + status = ipdb_write(pdbP, comp, stdout); + + if (status != 0) + pm_error("Failed to write PDB. %s.", ipdb_err(status)); + + if (comp == IPDB_COMPMAYBE && !ipdb_compressed(pdbP)) + pm_message("Image too complex to be compressed."); + + ipdb_free(pdbP); + + pm_close(ifP); + + return EXIT_SUCCESS; +} diff --git a/doc/HISTORY b/doc/HISTORY index befd4d3e..923d1612 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -6,12 +6,7 @@ CHANGE HISTORY not yet BJH Release 10.52.00 - - libnetpbm: add pm_readbiglong2, pm_readbiglong2u, - pm_readlittlelong2, pm_readlittlelong2u: These use the proper - 32 bit integer types instead of "long". (But the old ones - still work in legacy code because long is always at least 32 - bits). + Add pamtopdbimg. Add pamrecolor. Thanks Scott Pakin. @@ -22,6 +17,12 @@ not yet BJH Release 10.52.00 Mac OS X, because pnmtopng per se doesn't know about libz -- it's used by libpng. + libnetpbm: add pm_readbiglong2, pm_readbiglong2u, + pm_readlittlelong2, pm_readlittlelong2u: These use the proper + 32 bit integer types instead of "long". (But the old ones + still work in legacy code because long is always at least 32 + bits). + ppmtompeg: fix crash with free of unallocated memory. Broken after 10.18, not later than 10.26. -- cgit 1.4.1