Add an error check for correct formatting in Deflate uncompressed
[sgt/halibut] / winhelp.c
index 9bf1bd0..26c87f3 100644 (file)
--- a/winhelp.c
+++ b/winhelp.c
@@ -75,7 +75,7 @@
 #include "winhelp.h"
 #include "tree234.h"
 
-#ifdef TESTMODE
+#ifdef WINHELP_TESTMODE
 /*
  * This lot is useful for testing. Something like it will also be
  * needed to use this module standalone.
@@ -183,6 +183,7 @@ struct WHLP_tag {
     int para_flags;
     int para_attrs[7];
     int ncontexts;
+    int picture_index;
 };
 
 /* Functions to return the index and leaf data for B-tree contents. */
@@ -196,6 +197,14 @@ static void whlp_file_add(struct file *f, const void *data, int len);
 static void whlp_file_add_char(struct file *f, int data);
 static void whlp_file_add_short(struct file *f, int data);
 static void whlp_file_add_long(struct file *f, int data);
+static void whlp_file_add_cushort(struct file *f, int data);
+#if 0 /* currently unused */
+static void whlp_file_add_csshort(struct file *f, int data);
+#endif
+static void whlp_file_add_culong(struct file *f, int data);
+#if 0 /* currently unused */
+static void whlp_file_add_cslong(struct file *f, int data);
+#endif
 static void whlp_file_fill(struct file *f, int len);
 static void whlp_file_seek(struct file *f, int pos, int whence);
 static int whlp_file_offset(struct file *f);
@@ -580,7 +589,7 @@ void whlp_browse_link(WHLP h, WHLP_TOPIC before, WHLP_TOPIC after)
 static void whlp_linkdata(WHLP h, int which, int c)
 {
     int *len = (which == 1 ? &h->link->len1 : &h->link->len2);
-    char *data = (which == 1 ? h->linkdata1 : h->linkdata2);
+    unsigned char *data = (which == 1 ? h->linkdata1 : h->linkdata2);
     assert(*len < TOPIC_BLKSIZE);
     data[(*len)++] = c;
 }
@@ -797,6 +806,94 @@ void whlp_tab(WHLP h)
     whlp_linkdata(h, 1, 0x83);
 }
 
+int whlp_add_picture(WHLP h, int wd, int ht, const void *vpicdata,
+                    const unsigned long *palette)
+{
+    struct file *f;
+    char filename[80];
+    const unsigned char *picdata = (const unsigned char *)vpicdata;
+    int picstart, picoff, imgoff, imgstart;
+    int palettelen;
+    int i, index;
+    int wdrounded;
+
+    /*
+     * Determine the limit of the colour palette.
+     */
+    palettelen = -1;
+    for (i = 0; i < wd*ht; i++)
+       if (palettelen < picdata[i])
+           palettelen = picdata[i];
+    palettelen++;
+
+    /*
+     * Round up the width to the next multiple of 4.
+     */
+    wdrounded = (wd + 3) & ~3;
+
+    index = h->picture_index++;
+    sprintf(filename, "bm%d", index);
+
+    f = whlp_new_file(h, filename);
+    whlp_file_add_short(f, 0x706C);    /* magic number */
+    whlp_file_add_short(f, 1);        /* number of pictures */
+    picoff = whlp_file_offset(f);
+    whlp_file_add_long(f, 0);         /* offset of first (only) picture */
+    picstart = whlp_file_offset(f);
+    whlp_file_add_char(f, 6);         /* DIB */
+    whlp_file_add_char(f, 0);         /* no packing */
+    whlp_file_add_culong(f, 100);      /* xdpi */
+    whlp_file_add_culong(f, 100);      /* ydpi */
+    whlp_file_add_cushort(f, 1);       /* planes (?) */
+    whlp_file_add_cushort(f, 8);       /* bitcount */
+    whlp_file_add_culong(f, wd);       /* width */
+    whlp_file_add_culong(f, ht);       /* height */
+    whlp_file_add_culong(f, palettelen);/* colours used */
+    whlp_file_add_culong(f, palettelen);/* colours important */
+    whlp_file_add_culong(f, wdrounded*ht);    /* `compressed' data size */
+    whlp_file_add_culong(f, 0);               /* hotspot size (no hotspots) */
+    imgoff = whlp_file_offset(f);
+    whlp_file_add_long(f, 0);         /* offset of `compressed' data */
+    whlp_file_add_long(f, 0);         /* offset of hotspot data (none) */
+    for (i = 0; i < palettelen; i++)
+       whlp_file_add_long(f, palette[i]);
+    imgstart = whlp_file_offset(f);
+    /*
+     * Windows Help files, like BMP, start from the bottom scanline.
+     */
+    for (i = ht; i-- > 0 ;) {
+       whlp_file_add(f, picdata + i*wd, wd);
+       if (wd < wdrounded)
+           whlp_file_add(f, "\0\0\0", wdrounded - wd);
+    }
+
+    /* Now go back and fix up internal offsets */
+    whlp_file_seek(f, picoff, 0);
+    whlp_file_add_long(f, picstart);
+    whlp_file_seek(f, imgoff, 0);
+    whlp_file_add_long(f, imgstart - picstart);
+    whlp_file_seek(f, 0, 2);
+
+    return index;
+}
+
+void whlp_ref_picture(WHLP h, int picid)
+{
+    /*
+     * Write a NUL into linkdata2.
+     */
+    whlp_linkdata(h, 2, 0);
+    /*
+     * Write the formatting command and its followup data to
+     * specify a picture in a separate file.
+     */
+    whlp_linkdata(h, 1, 0x86);
+    whlp_linkdata(h, 1, 3);           /* type (picture without hotspots) */
+    whlp_linkdata_cslong(h, 1, 4);
+    whlp_linkdata_short(h, 1, 0);
+    whlp_linkdata_short(h, 1, picid);
+}
+
 void whlp_text(WHLP h, char *text)
 {
     while (*text) {
@@ -1275,7 +1372,7 @@ static void whlp_make_btree(struct file *f, int flags, int pagesize,
     int npages = 0, pagessize = 0;
     int npages_this_level, nentries, nlevels;
     int total_leaf_entries;
-    char btdata[MAX_PAGE_SIZE];
+    unsigned char btdata[MAX_PAGE_SIZE];
     int btlen;
     int page_start, fixups_offset, unused_bytes;
     void *element;
@@ -1538,6 +1635,46 @@ static void whlp_file_add_long(struct file *f, int data)
     whlp_file_add(f, s, 4);
 }
 
+static void whlp_file_add_cushort(struct file *f, int data)
+{
+    if (data <= 0x7F) {
+       whlp_file_add_char(f, data*2);
+    } else {
+       whlp_file_add_char(f, 1 + (data%128 * 2));
+       whlp_file_add_char(f, data/128);
+    }
+}
+
+#if 0                                 /* currently unused */
+static void whlp_file_add_csshort(struct file *f, int data)
+{
+    if (data >= -0x40 && data <= 0x3F)
+       whlp_file_add_cushort(f, data+64);
+    else
+       whlp_file_add_cushort(f, data+16384);
+}
+#endif
+
+static void whlp_file_add_culong(struct file *f, int data)
+{
+    if (data <= 0x7FFF) {
+       whlp_file_add_short(f, data*2);
+    } else {
+       whlp_file_add_short(f, 1 + (data%32768 * 2));
+       whlp_file_add_short(f, data/32768);
+    }
+}
+
+#if 0                                 /* currently unused */
+static void whlp_file_add_cslong(struct file *f, int data)
+{
+    if (data >= -0x4000 && data <= 0x3FFF)
+       whlp_file_add_culong(f, data+16384);
+    else
+       whlp_file_add_culong(f, data+67108864);
+}
+#endif
+
 static void whlp_file_fill(struct file *f, int len)
 {
     if (f->pos + len > f->size) {
@@ -1599,6 +1736,7 @@ WHLP whlp_new(void)
     ret->prevtopic = NULL;
     ret->ncontexts = 0;
     ret->link = NULL;
+    ret->picture_index = 0;
 
     return ret;
 }
@@ -1817,9 +1955,15 @@ void whlp_abandon(WHLP h)
     sfree(h);
 }
 
-#ifdef TESTMODE
+#ifdef WINHELP_TESTMODE
+
+#ifdef PICTURE_FROM_CMDLINE
+#include "png.h"
+#include "colquant.h"
+#include "dither.h"
+#endif
 
-int main(void)
+int main(int argc, char **argv)
 {
     WHLP h;
     WHLP_TOPIC t1, t2, t3;
@@ -2123,7 +2267,74 @@ int main(void)
 
     whlp_begin_para(h, WHLP_PARA_SCROLL);
     whlp_set_font(h, 1);
-    whlp_text(h, "This third topic is almost as boring as the first. Woo!");
+    whlp_text(h, "This third topic is not nearly as boring as the first, "
+             "because it has a picture: ");
+    {
+#ifndef PICTURE_FROM_CMDLINE
+       const unsigned long palette[] = {
+           0xFF0000,
+           0xFFFF00,
+           0x00FF00,
+           0x00FFFF,
+           0x0000FF,
+       };
+       const unsigned char picture[] = {
+           0, 0, 0, 0, 1, 2, 3, 4,
+           0, 0, 0, 0, 1, 2, 3, 4,
+           0, 0, 0, 0, 1, 2, 3, 4,
+           0, 0, 0, 1, 2, 3, 4, 4,
+           0, 0, 0, 1, 2, 3, 4, 4,
+           0, 0, 0, 1, 2, 3, 4, 4,
+           0, 0, 1, 2, 3, 4, 4, 4,
+           0, 0, 1, 2, 3, 4, 4, 4,
+           0, 0, 1, 2, 3, 4, 4, 4,
+           0, 1, 2, 3, 4, 4, 4, 4,
+           0, 1, 2, 3, 4, 4, 4, 4,
+           0, 1, 2, 3, 4, 4, 4, 4,
+       };
+       int wid = 8, ht = 12;
+#else
+       png_pixel ppalette[256];
+       unsigned long palette[256];
+       unsigned char *picture;
+       png *png;
+       colquant *cq;
+       int plen, i, err, wid, ht;
+
+       if (argc < 2) {
+           fprintf(stderr, "in this mode I need a .png file on the"
+                   " command line\n");
+           return 1;
+       }
+       png = png_decode_file(argv[1], &err);
+       if (!png) {
+           fprintf(stderr, "%s: PNG read error: %s\n", argv[1],
+                   png_error_msg[err]);
+           return 1;
+       }
+
+       cq = colquant_new(256, 8);
+       colquant_data(cq, png->pixels, png->width * png->height);
+       plen = colquant_get_palette(cq, ppalette);
+       colquant_free(cq);
+       assert(plen <= 256);
+       for (i = 0; i < plen; i++) {
+           palette[i] = ppalette[i].r >> 8;
+           palette[i] <<= 8;
+           palette[i] |= ppalette[i].g >> 8;
+           palette[i] <<= 8;
+           palette[i] |= ppalette[i].b >> 8;
+       }
+       picture = malloc(png->width * png->height);
+       dither_image(png->width, png->height, png->pixels,
+                    ppalette, plen, picture);
+       wid = png->width;
+       ht = png->height;
+       png_free(png);
+
+#endif
+       whlp_ref_picture(h, whlp_add_picture(h, wid, ht, picture, palette));
+    }
     whlp_end_para(h);
 
     /*