ftp://netsw.org/x11/tools/desktop/xscreensaver-4.07.tar.gz
[xscreensaver] / hacks / barcode.c
diff --git a/hacks/barcode.c b/hacks/barcode.c
new file mode 100644 (file)
index 0000000..218e3a1
--- /dev/null
@@ -0,0 +1,1780 @@
+/* barcode, draw some barcodes
+ * by Dan Bornstein, danfuzz@milk.com
+ * Copyright (c) 2003 Dan Bornstein. All rights reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ * See the included man page for more details.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "screenhack.h"
+#include <X11/Xutil.h>
+
+
+
+/* parameters that are user configurable */
+
+/* delay (usec) between iterations */
+static int delay;
+
+static int scroll_p;
+
+
+
+/* non-user-modifiable immutable definitions */
+
+#define FLOAT double
+
+/* random float in the range (0..1) */
+#define RAND_FLOAT_01 \
+        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
+
+#define BARCODE_WIDTH (164)
+#define BARCODE_HEIGHT (69)
+#define MAX_MAG (6)
+
+/* width and height of the window */
+static int windowWidth;
+static int windowHeight;
+
+static Display *display;        /* the display to draw on */
+static Window window;           /* the window to draw on */
+static Visual *visual;          /* the visual to use */
+static Screen *screen;          /* the screen to draw on */
+static Colormap cmap;           /* the colormap of the window */
+
+static GC theGC;                /* GC for drawing */
+
+
+
+/* simple bitmap structure */
+
+typedef struct
+{
+    int width;
+    int height;
+    int widthBytes;
+    unsigned char *buf;
+}
+Bitmap;
+
+
+
+/* the model */
+
+typedef struct
+{
+    int x;          /* x coordinate of the left of the barcode */
+    int y;          /* y coordinate of the left of the barcode */
+    int mag;        /* magnfication factor */
+    Bitmap *bitmap; /* the bitmap */
+    char code[128]; /* the barcode string */
+    unsigned long pixel;  /* the color */
+}
+Barcode;
+
+static Barcode *barcodes; /* array of barcodes */
+static int barcode_count; /* how many barcodes are currently active */
+static int barcode_max;   /* the maximum number of active barcodes */
+
+static XImage *theImage;  /* ginormo image for drawing */
+static Bitmap *theBitmap; /* ginormo bitmap for drawing */
+
+/* a bunch of words */
+static char *words[] = 
+{
+    "abdomen",
+    "abeyance",
+    "abhorrent",
+    "abrasive",
+    "abstract",
+    "acid",
+    "addiction",
+    "alertness",
+    "Algeria",
+    "anxiety",
+    "aorta",
+    "argyle socks",
+    "attrition",
+    "bamboo",
+    "bangle",
+    "bankruptcy",
+    "baptism",
+    "beer",
+    "bellicose",
+    "bells",
+    "belly",
+    "bread",
+    "bubba",
+    "burrito",
+    "California",
+    "capybara",
+    "cardinality",
+    "caribou",
+    "carnage",
+    "chocolate",
+    "constriction",
+    "contrition",
+    "corpse",
+    "cowboy",
+    "cozy",
+    "crabapple",
+    "craziness",
+    "Death",
+    "Decker",
+    "decoded",
+    "decoy",
+    "defenestration",
+    "dependency",
+    "despair",
+    "desperation",
+    "disease",
+    "doberman",
+    "dreams",
+    "drench",
+    "drugs",
+    "easy",
+    "ebony",
+    "elliptic",
+    "eloquence",
+    "emergency",
+    "eureka",
+    "excommunicate",
+    "fat",
+    "fatherland",
+    "Faust",
+    "fear",
+    "fever",
+    "flatulence",
+    "fluff",
+    "fnord",
+    "freedom",
+    "fruit",
+    "fruit",
+    "gauche",
+    "gawk",
+    "gaze",
+    "gerbils",
+    "GOD",
+    "goggles",
+    "goobers",
+    "gorilla",
+    "halibut",
+    "handmaid",
+    "hapless",
+    "happiness",
+    "hate",
+    "helplessness",
+    "hermaphrodite",
+    "Hindi",
+    "hope",
+    "hysteria",
+    "icepick",
+    "ignorance",
+    "importance",
+    "impossibility",
+    "inkling",
+    "insurrection",
+    "intoxicant",
+    "ire",
+    "irritant",
+    "jade",
+    "jaundice",
+    "Joyce",
+    "kaput",
+    "kitchenette",
+    "kiwi",
+    "lathe",
+    "lattice",
+    "lemming",
+    "liquidation",
+    "love",
+    "lozenge",
+    "magazine",
+    "magnesium",
+    "malfunction",
+    "marmot",
+    "marshmallow",
+    "merit",
+    "mescaline",
+    "milk",
+    "mischief",
+    "mistrust",
+    "money",
+    "monkey",
+    "monkeybutter",
+    "multiple",
+    "nature",
+    "neuron",
+    "noise",
+    "nomenclature",
+    "nutria",
+    "obey",
+    "ocelot",
+    "offspring",
+    "overseer",
+    "pain",
+    "pajamas",
+    "passenger",
+    "passion",
+    "Passover",
+    "Prozac",
+    "peace",
+    "penance",
+    "persimmon",
+    "petticoat",
+    "pharmacist",
+    "pitchfork",
+    "plague",
+    "Poindexter",
+    "precept",
+    "prison",
+    "prophecy",
+    "quadratic",
+    "quagmire",
+    "quarantine",
+    "quartz",
+    "rabies",
+    "radish",
+    "rage",
+    "readout",
+    "reality",
+    "reject",
+    "rejection",
+    "respect",
+    "revolution",
+    "roadrunner",
+    "rule",
+    "sanguine",
+    "savor",
+    "scab",
+    "scalar",
+    "Scandinavia",
+    "security",
+    "sediment",
+    "sickness",
+    "silicone",
+    "slack",
+    "slander",
+    "slavery",
+    "sledgehammer",
+    "smelly socks",
+    "sorrow",
+    "stamen",
+    "standardization",
+    "subversion",
+    "suffering",
+    "surrender",
+    "surveilance",
+    "synthesis",
+    "tenant",
+    "tendril",
+    "terror",
+    "terrorism",
+    "terrorist",
+    "the unknown",
+    "toast",
+    "topography",
+    "truism",
+    "turgid",
+    "underbrush",
+    "underling",
+    "unguent",
+    "unusual",
+    "unworthy",
+    "uplink",
+    "urge",
+    "valor",
+    "variance",
+    "vastness",
+    "vaudeville",
+    "vegetarian",
+    "venom",
+    "verifiability",
+    "viagra",
+    "vibrator",
+    "victim",
+    "vignette",
+    "villainy",
+    "W.A.S.T.E.",
+    "wagon",
+    "waiver",
+    "warehouse",
+    "waste",
+    "waveform",
+    "whiffle ball",
+    "whorl",
+    "windmill",
+    "wistful",
+    "worm",
+    "worship",
+    "worship",
+    "Xanax",
+    "Xerxes",
+    "Xhosa",
+    "xylophone",
+    "yellow",
+    "yesterday",
+    "your nose",
+    "Zanzibar",
+    "zeal",
+    "zebra",
+    "zest",
+    "zinc"
+};
+
+#define WORD_COUNT (sizeof(words) / sizeof(char *))
+
+
+
+/* ----------------------------------------------------------------------------
+ * bitmap manipulation
+ */
+
+/* construct a new bitmap */
+Bitmap *makeBitmap (int width, int height)
+{
+    Bitmap *result = malloc (sizeof (Bitmap));
+    result->width = width;
+    result->height = height;
+    result->widthBytes = (width + 7) / 8;
+    result->buf = calloc (1, height * result->widthBytes);
+    return result;
+}
+
+/* clear a bitmap */
+void bitmapClear (Bitmap *b)
+{
+    memset (b->buf, 0, b->widthBytes * b->height);
+}
+
+/* free a bitmap */
+void bitmapFree (Bitmap *b)
+{
+    free (b->buf);
+    free (b);
+}
+
+/* get the byte value at the given byte-offset coordinates in the given
+ * bitmap */
+int bitmapGetByte (Bitmap *b, int xByte, int y)
+{
+    if ((xByte < 0) || 
+       (xByte >= b->widthBytes) || 
+       (y < 0) || 
+       (y >= b->height))
+    {
+       /* out-of-range get returns 0 */
+       return 0;
+    }
+
+    return b->buf[b->widthBytes * y + xByte];
+}
+
+/* get the bit value at the given coordinates in the given bitmap */
+int bitmapGet (Bitmap *b, int x, int y)
+{
+    int xbyte = x >> 3;
+    int xbit = x & 0x7;
+    int byteValue = bitmapGetByte (b, xbyte, y);
+
+    return (byteValue & (1 << xbit)) >> xbit;
+}
+
+/* set the bit value at the given coordinates in the given bitmap */
+void bitmapSet (Bitmap *b, int x, int y, int value)
+{
+    int xbyte = x >> 3;
+    int xbit = x & 0x7;
+
+    if ((x < 0) || 
+       (x >= b->width) || 
+       (y < 0) || 
+       (y >= b->height))
+    {
+       /* ignore out-of-range set */
+       return;
+    }
+
+    if (value)
+    {
+       b->buf[b->widthBytes * y + xbyte] |= 1 << xbit;
+    }
+    else
+    {
+       b->buf[b->widthBytes * y + xbyte] &= ~(1 << xbit);
+    }
+}
+
+/* copy the given rectangle to the given destination from the given source. */
+void bitmapCopyRect (Bitmap *dest, int dx, int dy,
+                    Bitmap *src, int sx, int sy, int width, int height)
+{
+    int x, y;
+
+    for (y = 0; y < height; y++)
+    {
+       for (x = 0; x < width; x++)
+       {
+           bitmapSet (dest, x + dx, y + dy, bitmapGet (src, x + sx, y + sy));
+       }
+    }
+}
+
+/* draw a vertical line in the given bitmap */
+void bitmapVlin (Bitmap *b, int x, int y1, int y2)
+{
+    while (y1 <= y2)
+    {
+        bitmapSet (b, x, y1, 1);
+        y1++;
+    }
+}
+
+/* scale a bitmap into another bitmap */
+void bitmapScale (Bitmap *dest, Bitmap *src, int mag)
+{
+    int x, y, x2, y2;
+
+    for (y = 0; y < src->height; y++)
+    {
+       for (x = 0; x < src->width; x++)
+       {
+           int v = bitmapGet (src, x, y);
+           for (x2 = 0; x2 < mag; x2++) 
+           {
+               for (y2 = 0; y2 < mag; y2++) 
+               {
+                   bitmapSet (dest, x * mag + x2, y * mag + y2, v);
+               }
+           }
+       }
+    }
+}
+
+
+/* ----------------------------------------------------------------------------
+ * character generation
+ */
+
+static unsigned char font5x8Buf[] = 
+{
+   0x1e, 0x01, 0x06, 0x01, 0x1e, 0x00, 0x1e, 0x01, 0x06, 0x01, 0x1e, 0x00,
+   0x1e, 0x01, 0x1e, 0x01, 0x1e, 0x00, 0x01, 0x00, 0x1f, 0x08, 0x04, 0x08,
+   0x1f, 0x00, 0x11, 0x1f, 0x11, 0x00, 0x1f, 0x01, 0x01, 0x00, 0x1f, 0x04,
+   0x0a, 0x11, 0x00, 0x01, 0x00, 0x0e, 0x11, 0x11, 0x00, 0x0e, 0x11, 0x11,
+   0x0e, 0x00, 0x1f, 0x08, 0x04, 0x08, 0x1f, 0x00, 0x44, 0x41, 0x4e, 0x20,
+   0x42, 0x4f, 0x52, 0x4e, 0x53, 0x54, 0x45, 0x49, 0x4e, 0x21, 0x21, 0x00,
+   0x66, 0x6e, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00,
+   0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x00, 0x05, 0x05, 0x05, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x0f, 0x05, 0x0f, 0x05, 0x05, 0x00,
+   0x02, 0x0f, 0x01, 0x0f, 0x08, 0x0f, 0x04, 0x00, 0x0b, 0x0b, 0x08, 0x06,
+   0x01, 0x0d, 0x0d, 0x00, 0x03, 0x05, 0x02, 0x05, 0x0d, 0x05, 0x0b, 0x00,
+   0x04, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x04, 0x00, 0x02, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x00,
+   0x00, 0x09, 0x06, 0x0f, 0x06, 0x09, 0x00, 0x00, 0x00, 0x02, 0x02, 0x07,
+   0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x06, 0x00,
+   0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x04, 0x06, 0x02, 0x01, 0x01, 0x00,
+   0x0f, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0f, 0x00, 0x06, 0x04, 0x04, 0x04,
+   0x04, 0x04, 0x0f, 0x00, 0x0f, 0x09, 0x08, 0x0f, 0x01, 0x09, 0x0f, 0x00,
+   0x0f, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x0f, 0x00, 0x09, 0x09, 0x09, 0x0f,
+   0x08, 0x08, 0x08, 0x00, 0x0f, 0x09, 0x01, 0x0f, 0x08, 0x09, 0x0f, 0x00,
+   0x03, 0x01, 0x01, 0x0f, 0x09, 0x09, 0x0f, 0x00, 0x0f, 0x09, 0x09, 0x0c,
+   0x04, 0x04, 0x04, 0x00, 0x0f, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x0f, 0x00,
+   0x0f, 0x09, 0x09, 0x0f, 0x08, 0x08, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00,
+   0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x04, 0x06, 0x00,
+   0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00,
+   0x0f, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x04, 0x02, 0x01, 0x00,
+   0x0f, 0x09, 0x08, 0x0e, 0x02, 0x00, 0x02, 0x00, 0x0f, 0x09, 0x0d, 0x0d,
+   0x0d, 0x01, 0x0f, 0x00, 0x0f, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x09, 0x00,
+   0x07, 0x09, 0x09, 0x07, 0x09, 0x09, 0x07, 0x00, 0x0f, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x0f, 0x00, 0x07, 0x09, 0x09, 0x09, 0x09, 0x09, 0x07, 0x00,
+   0x0f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x0f, 0x00, 0x0f, 0x01, 0x01, 0x0f,
+   0x01, 0x01, 0x01, 0x00, 0x0f, 0x01, 0x01, 0x0d, 0x09, 0x09, 0x0f, 0x00,
+   0x09, 0x09, 0x09, 0x0f, 0x09, 0x09, 0x09, 0x00, 0x07, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x07, 0x00, 0x0e, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x00,
+   0x09, 0x09, 0x09, 0x07, 0x09, 0x09, 0x09, 0x00, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x0f, 0x00, 0x09, 0x0f, 0x0f, 0x0f, 0x09, 0x09, 0x09, 0x00,
+   0x09, 0x0b, 0x0d, 0x09, 0x09, 0x09, 0x09, 0x00, 0x0f, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x0f, 0x00, 0x0f, 0x09, 0x09, 0x0f, 0x01, 0x01, 0x01, 0x00,
+   0x0f, 0x09, 0x09, 0x09, 0x0b, 0x05, 0x0b, 0x00, 0x07, 0x09, 0x09, 0x07,
+   0x09, 0x09, 0x09, 0x00, 0x0f, 0x01, 0x01, 0x0f, 0x08, 0x08, 0x0f, 0x00,
+   0x0f, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x09, 0x09, 0x09, 0x09,
+   0x09, 0x09, 0x0f, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x05, 0x02, 0x00,
+   0x09, 0x09, 0x09, 0x09, 0x0f, 0x0f, 0x09, 0x00, 0x09, 0x09, 0x05, 0x06,
+   0x0a, 0x09, 0x09, 0x00, 0x09, 0x09, 0x09, 0x0f, 0x08, 0x08, 0x0f, 0x00,
+   0x0f, 0x08, 0x08, 0x06, 0x01, 0x01, 0x0f, 0x00, 0x0e, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x0e, 0x00, 0x01, 0x01, 0x02, 0x06, 0x04, 0x08, 0x08, 0x00,
+   0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07, 0x00, 0x02, 0x05, 0x05, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
+   0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08,
+   0x0f, 0x09, 0x0f, 0x00, 0x01, 0x01, 0x0f, 0x09, 0x09, 0x09, 0x0f, 0x00,
+   0x00, 0x00, 0x0f, 0x01, 0x01, 0x01, 0x0f, 0x00, 0x08, 0x08, 0x0f, 0x09,
+   0x09, 0x09, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x09, 0x0f, 0x01, 0x0f, 0x00,
+   0x0e, 0x02, 0x0f, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x09,
+   0x09, 0x0f, 0x08, 0x0c, 0x01, 0x01, 0x0f, 0x09, 0x09, 0x09, 0x09, 0x00,
+   0x02, 0x00, 0x03, 0x02, 0x02, 0x02, 0x07, 0x00, 0x04, 0x00, 0x04, 0x04,
+   0x04, 0x04, 0x05, 0x07, 0x01, 0x01, 0x09, 0x05, 0x03, 0x05, 0x09, 0x00,
+   0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07, 0x00, 0x00, 0x00, 0x09, 0x0f,
+   0x0f, 0x09, 0x09, 0x00, 0x00, 0x00, 0x0f, 0x09, 0x09, 0x09, 0x09, 0x00,
+   0x00, 0x00, 0x0f, 0x09, 0x09, 0x09, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x09,
+   0x09, 0x0f, 0x01, 0x01, 0x00, 0x00, 0x0f, 0x09, 0x09, 0x0f, 0x08, 0x08,
+   0x00, 0x00, 0x0f, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x01,
+   0x0f, 0x08, 0x0f, 0x00, 0x00, 0x02, 0x0f, 0x02, 0x02, 0x02, 0x0e, 0x00,
+   0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x0f, 0x00, 0x00, 0x00, 0x09, 0x09,
+   0x09, 0x05, 0x02, 0x00, 0x00, 0x00, 0x09, 0x09, 0x0f, 0x0f, 0x09, 0x00,
+   0x00, 0x00, 0x09, 0x09, 0x06, 0x09, 0x09, 0x00, 0x00, 0x00, 0x09, 0x09,
+   0x09, 0x0f, 0x08, 0x0c, 0x00, 0x00, 0x0f, 0x08, 0x06, 0x01, 0x0f, 0x00,
+   0x08, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x00, 0x02, 0x02, 0x02, 0x02,
+   0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x04, 0x02, 0x02, 0x01, 0x00,
+   0x00, 0x0a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
+   0x0f, 0x0f, 0x0f, 0x00
+};
+
+static Bitmap font5x8 = { 8, 1024, 1, font5x8Buf };
+
+/* draw the given 5x8 character at the given coordinates */
+void bitmapDrawChar5x8 (Bitmap *b, int x, int y, char c)
+{
+    bitmapCopyRect (b, x, y, &font5x8, 0, c * 8, 5, 8);
+}
+
+/* draw a string of 5x8 characters at the given coordinates */
+void bitmapDrawString5x8 (Bitmap *b, int x, int y, char *str)
+{
+    int origx = x;
+
+    while (*str != '\0')
+    {
+       char c = *str;
+       if (c == '\n')
+       {
+           x = origx;
+           y += 8;
+       }
+       else
+       {
+           if (c < ' ')
+           {
+               c = ' ';
+           }
+
+           bitmapDrawChar5x8 (b, x, y, c);
+           x += 5;
+       }
+       str++;
+    }
+}
+
+
+
+/* ----------------------------------------------------------------------------
+ * upc/ean symbologies
+ */
+
+/* A quick lesson in UPC and EAN barcodes: 
+ *
+ * Each digit consists of 2 bars and 2 spaces, taking a total width of 7
+ * times the width of the thinnest possible bar or space. There are three
+ * different possible representations for each digit, used depending on
+ * what side of a two-sided barcode the digit is used on, and to encode
+ * checksum or other information in some cases. The three forms are
+ * related. Taking as the "base" form the pattern as seen on the right-hand
+ * side of a UPC-A barcode, the other forms are the inverse of the base
+ * (that is, bar becomes space and vice versa) and the mirror image of the
+ * base. Still confused? Here's a complete table, where 0 means space and 1
+ * means bar:
+ *
+ *      Left-A   Left-B   Right
+ *      -------  -------  -------
+ *   0  0001101  0100111  1110010
+ *   1  0011001  0110011  1100110
+ *   2  0010011  0011011  1101100
+ *   3  0111101  0100001  1000010
+ *   4  0100011  0011101  1011100
+ *   5  0110001  0111001  1001110
+ *   6  0101111  0000101  1010000
+ *   7  0111011  0010001  1000100
+ *   8  0110111  0001001  1001000
+ *   9  0001011  0010111  1110100
+ *
+ * A UPC-A barcode consists of 6 patterns from Left-A on the left-hand side,
+ * 6 patterns from Right on the right-hand side, a guard pattern of 01010
+ * in the middle, and a guard pattern of 101 on each end. The 12th digit
+ * checksum is calculated as follows: Take the 1st, 3rd, ... 11th digits,
+ * sum them and multiplying by 3, and add that to the sum of the other digits.
+ * Subtract the final digit from 10, and that is the checksum digit. (If
+ * the last digit of the sum is 0, then the check digit is 0.)
+ *
+ * An EAN-13 barcode is just like a UPC-A barcode, except that the characters
+ * on the left-hand side have a pattern of Left-A and Left-B that encodes
+ * an extra first digit. Note that an EAN-13 barcode with the first digit
+ * of 0 is exactly the same as the UPC-A barcode of the rightmost 12 digits.
+ * The patterns to encode the first digit are as follows:
+ *
+ *      Left-Hand 
+ *      Digit Position
+ *      1 2 3 4 5 6
+ *      - - - - - -
+ *   0  a a a a a a
+ *   1  a a b a b b
+ *   2  a a b b a b
+ *   3  a a b b b a
+ *   4  a b a a b b
+ *   5  a b b a a b
+ *   6  a b b b a a
+ *   7  a b a b a b
+ *   8  a b a b b a
+ *   9  a b b a b a
+ *
+ * The checksum for EAN-13 is just like UPC-A, except the 2nd, 4th, ... 12th
+ * digits are multiplied by 3 instead of the other way around.
+ *
+ * An EAN-8 barcode is just like a UPC-A barcode, except there are only 4
+ * digits in each half. Unlike EAN-13, there's no nonsense about different
+ * left-hand side patterns, either.
+ *
+ * A UPC-E barcode contains 6 explicit characters between a guard of 101
+ * on the left and 010101 on the right. The explicit characters are the
+ * middle six characters of the code. The first and last characters are
+ * encoded in the parity pattern of the six characters. There are two
+ * sets of parity patterns, one to use if the first digit of the number
+ * is 0, and another if it is 1. (UPC-E barcodes may only start with a 0
+ * or 1.) The patterns are as follows:
+ *
+ *      First digit 0     First digit 1
+ *      Explicit Digit    Explicit Digit
+ *      Position          Position
+ *      1 2 3 4 5 6       1 2 3 4 5 6
+ *      - - - - - -       - - - - - -
+ *   0  b b b a a a       a a a b b b
+ *   1  b b a b a a       a a b a b b
+ *   2  b b a a b a       a a b b a b
+ *   3  b b a a a b       a a b b b a
+ *   4  b a b b a a       a b a a b b
+ *   5  b a a b b a       a b b a a b
+ *   6  b a a a b b       a b b b a a
+ *   7  b a b a b a       a b a b a b
+ *   8  b a b a a b       a b a b b a
+ *   9  b a a b a b       a b b a b a
+ *
+ * (Note that the two sets are the complements of each other. Also note
+ * that the first digit 1 patterns are mostly the same as the EAN-13
+ * first digit patterns.) The UPC-E check digit (the final digit encoded in
+ * the parity pattern) is the same as the UPC-A check digit for the
+ * expanded form of the UPC-E number. The expanstion is as follows, based
+ * on the last explicit digit (the second to last digit) in the encoded
+ * number:
+ *
+ *               Corresponding
+ *   UPC-E form  UPC-A form
+ *   ----------  -------------
+ *   XABCDE0Y    XAB00000CDEY
+ *   XABCDE1Y    XAB10000CDEY
+ *   XABCDE2Y    XAB20000CDEY
+ *   XABCDE3Y    XABC00000DEY
+ *   XABCDE4Y    XABCD00000EY
+ *   XABCDE5Y    XABCDE00005Y
+ *   XABCDE6Y    XABCDE00006Y
+ *   XABCDE7Y    XABCDE00007Y
+ *   XABCDE8Y    XABCDE00008Y
+ *   XABCDE9Y    XABCDE00009Y 
+ *
+ * All UPC/EAN barcodes may have an additional 2- or 5-digit supplemental
+ * code just to the right of the main barcode. The supplement starts about
+ * one digit-length (that is about 7 times the width of the thinnest bar)
+ * to the right of the main code, beginning with the guard pattern 1011.
+ * After that comes each digit, with a guard pattern of 01 between each,
+ * but not at the end. The digits are encoded using the left A and B
+ * characters to encode a parity pattern.
+ *
+ * For 2-digit supplements, the parity pattern is determined by the
+ * lower two bits of the numeric value of the code (e.g., 42 would use
+ * pattern 2):
+ *
+ *   Lower 2 bits  Parity Pattern
+ *   ------------  --------------
+ *   0 (bin 00)    a a
+ *   1 (bin 01)    a b
+ *   2 (bin 10)    b a
+ *   3 (bin 11)    b b
+ *
+ * For 5-digit supplements, the parity pattern is calculated in a similar
+ * manner to check digit calculation: The first, third, and fifth digits
+ * are summed and multiplied by 3; the second and fourth digits are summed
+ * and multiplied by nine; the parity digit is the sum of those two numbers,
+ * modulo 10. The parity pattern is then the last five patterns from the
+ * UPC-E final digit 0 table for the corresponding digit.
+ */
+
+/* enum to indicate which pattern set to use */
+typedef enum
+{
+    UPC_LEFT_A, UPC_LEFT_B, UPC_RIGHT
+}
+UpcSet;
+
+/* the Left A patterns */
+unsigned int upcLeftA[] = { 
+    0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b 
+};
+
+/* the Left B patterns */
+unsigned int upcLeftB[] = { 
+    0x27, 0x33, 0x1b, 0x21, 0x1d, 0x39, 0x05, 0x11, 0x09, 0x17
+};
+
+/* the Right patterns */
+unsigned int upcRight[] = { 
+    0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74
+};
+
+/* the EAN-13 first-digit patterns */
+unsigned int ean13FirstDigit[] = {
+    0x00, 0x0b, 0x0d, 0x0e, 0x13, 0x19, 0x1c, 0x15, 0x16, 0x1a
+};
+
+/* the UPC-E last-digit patterns for first digit 0 (complement for
+ * digit 1); also used for 5-digit supplemental check patterns */
+unsigned int upcELastDigit[] = {
+    0x38, 0x34, 0x32, 0x31, 0x2c, 0x26, 0x23, 0x2a, 0x29, 0x25
+};
+
+/* turn a character into an int representing its digit value; return
+ * 0 for things not in the range '0'-'9' */
+int charToDigit (char c)
+{
+    if ((c >= '0') && (c <= '9'))
+    {
+       return c - '0';
+    }
+    else
+    {
+       return 0;
+    }
+}
+
+/* draw the given digit character at the given coordinates; a '0' is
+ * used in place of any non-digit character */
+void drawDigitChar (Bitmap *b, int x, int y, char c)
+{
+    if ((c < '0') || (c > '9'))
+    {
+       c = '0';
+    }
+
+    bitmapDrawChar5x8 (b, x, y, c);
+}
+
+/* draw a upc/ean digit at the given coordinates */
+void drawUpcEanDigit (Bitmap *upcBitmap, int x, int y1, int y2, char n, 
+                     UpcSet set)
+{
+    unsigned int bits;
+    int i;
+    
+    n = charToDigit (n);
+    switch (set)
+    {
+       case UPC_LEFT_A: 
+           bits = upcLeftA[(int) n];
+           break;
+       case UPC_LEFT_B: 
+           bits = upcLeftB[(int) n];
+           break;
+        default /* case UPC_RIGHT */:
+           bits = upcRight[(int) n];
+           break;
+    }
+
+    for (i = 6; i >=0; i--)
+    {
+        if (bits & (1 << i))
+       {
+            bitmapVlin (upcBitmap, x, y1, y2);
+       }
+        x++;
+    }
+}
+
+/* report the width of the given supplemental code or 0 if it is a bad
+ * supplement form */
+int upcEanSupplementWidth (char *digits)
+{
+    switch (strlen (digits))
+    {
+       case 2: return 28; /* 8 + 4 + 2*7 + 1*2 */
+       case 5: return 55; /* 8 + 4 + 5*7 + 4*2 */
+       default: return 0;
+    }
+}
+
+/* draw the given supplemental barcode, including the textual digits */
+void drawUpcEanSupplementalBars (Bitmap *upcBitmap, char *digits, 
+                                int x, int y, int y2, int textAbove)
+{
+    int len = strlen (digits);
+    int i;
+    int parity;
+    int textY;
+    int textX;
+
+    if (textAbove)
+    {
+       textY = y;
+       y += 8;
+    }
+    else
+    {
+       y2 -= 8;
+       textY = y2 + 2;
+    }
+
+    x += 8; /* skip the space between the main and supplemental */
+
+    switch (len)
+    {
+       case 2: 
+       {
+           textX = x + 5;
+           parity = (charToDigit (digits[0]) * 10 + 
+                     charToDigit (digits[1])) & 0x3;
+           break;
+       }
+       case 5:
+       {
+           textX = x + 10;
+           parity = 
+               ((charToDigit (digits[0]) + charToDigit (digits[2]) + 
+                 charToDigit (digits[4])) * 3
+                + (charToDigit (digits[1]) + charToDigit (digits[3])) * 9)
+               % 10;
+           parity = upcELastDigit[parity];
+           break;
+       }
+       default:
+       {
+           printf("Bad supplement\n");
+           exit(1);
+           break;
+       }
+    }
+
+    /* header */
+    bitmapVlin (upcBitmap, x, y, y2);
+    bitmapVlin (upcBitmap, x + 2, y, y2);
+    bitmapVlin (upcBitmap, x + 3, y, y2);
+
+    for (i = 0; i < len; i++)
+    {
+       UpcSet lset = 
+           (parity & (1 << (len - 1 - i))) ? UPC_LEFT_B : UPC_LEFT_A;
+       int baseX = x + 2 + i * 9;
+
+       /* separator / end of header */
+       if (i == 0)
+       {
+           bitmapVlin (upcBitmap, baseX, y, y2);
+       }
+       bitmapVlin (upcBitmap, baseX + 1, y, y2);
+
+        drawUpcEanDigit (upcBitmap,
+                        baseX + 2, 
+                        y,
+                        y2,
+                        digits[i], 
+                        lset);
+
+        drawDigitChar (upcBitmap, textX + i*6, textY, digits[i]);
+    }
+}
+
+/* draw the actual barcode part of a UPC-A barcode */
+void drawUpcABars (Bitmap *upcBitmap, char *digits, int x, int y, 
+                  int barY2, int guardY2)
+{
+    int i;
+
+    /* header */
+    bitmapVlin (upcBitmap, x, y, guardY2);
+    bitmapVlin (upcBitmap, x + 2, y, guardY2);
+
+    /* center marker */
+    bitmapVlin (upcBitmap, x + 46, y, guardY2);
+    bitmapVlin (upcBitmap, x + 48, y, guardY2);
+
+    /* trailer */
+    bitmapVlin (upcBitmap, x + 92, y, guardY2);
+    bitmapVlin (upcBitmap, x + 94, y, guardY2);
+
+    for (i = 0; i < 6; i++)
+    {
+        drawUpcEanDigit (upcBitmap,
+                        x + 3 + i*7, 
+                        y,
+                        (i == 0) ? guardY2 : barY2,
+                        digits[i], 
+                        UPC_LEFT_A);
+        drawUpcEanDigit (upcBitmap,
+                        x + 50 + i*7, 
+                        y, 
+                        (i == 5) ? guardY2 : barY2,
+                        digits[i+6], 
+                        UPC_RIGHT);
+    }
+}
+
+/* make and return a full-height UPC-A barcode */
+int makeUpcAFull (Bitmap *dest, char *digits, int y)
+{
+    static int baseWidth = 108;
+    static int baseHeight = 60;
+
+    int height = baseHeight + y;
+    int i;
+
+    bitmapClear (dest);
+    drawUpcABars (dest, digits, 6, y, height - 10, height - 4);
+
+    drawDigitChar (dest, 0, height - 14, digits[0]);
+
+    for (i = 0; i < 5; i++)
+    {
+        drawDigitChar (dest, 18 + i*7, height - 7, digits[i+1]);
+        drawDigitChar (dest, 57 + i*7, height - 7, digits[i+6]);
+    }
+
+    drawDigitChar (dest, 103, height - 14, digits[11]);
+
+    return baseWidth;
+}
+
+/* make and return a UPC-A barcode */
+int makeUpcA (Bitmap *dest, char *digits, int y)
+{
+    int i;
+    unsigned int mul = 3;
+    unsigned int sum = 0;
+
+    for (i = 0; i < 11; i++)
+    {
+       sum += charToDigit (digits[i]) * mul;
+       mul ^= 2;
+    }
+
+    if (digits[11] == '?')
+    {
+        digits[11] = ((10 - (sum % 10)) % 10) + '0';
+    }
+
+    return makeUpcAFull (dest, digits, y);
+}
+
+/* draw the actual barcode part of a UPC-E barcode */
+void drawUpcEBars (Bitmap *upcBitmap, char *digits, int x, int y, 
+                  int barY2, int guardY2)
+{
+    int i;
+    int parityPattern = upcELastDigit[charToDigit(digits[7])];
+
+    if (digits[0] == '1')
+    {
+       parityPattern = ~parityPattern;
+    }
+
+    /* header */
+    bitmapVlin (upcBitmap, x, y, guardY2);
+    bitmapVlin (upcBitmap, x + 2, y, guardY2);
+
+    /* trailer */
+    bitmapVlin (upcBitmap, x + 46, y, guardY2);
+    bitmapVlin (upcBitmap, x + 48, y, guardY2);
+    bitmapVlin (upcBitmap, x + 50, y, guardY2);
+
+    for (i = 0; i < 6; i++)
+    {
+       UpcSet lset = 
+           (parityPattern & (1 << (5 - i))) ? UPC_LEFT_B : UPC_LEFT_A;
+
+        drawUpcEanDigit (upcBitmap,
+                        x + 3 + i*7, 
+                        y,
+                        barY2,
+                        digits[i + 1], 
+                        lset);
+    }
+}
+
+/* make and return a full-height UPC-E barcode */
+int makeUpcEFull (Bitmap *dest, char *digits, int y)
+{
+    static int baseWidth = 64;
+    static int baseHeight = 60;
+
+    int height = baseHeight + y;
+    int i;
+
+    bitmapClear (dest);
+    drawUpcEBars (dest, digits, 6, y, height - 10, height - 4);
+
+    drawDigitChar (dest, 0, height - 14, digits[0]);
+
+    for (i = 0; i < 6; i++)
+    {
+        drawDigitChar (dest, 11 + i*7, height - 7, digits[i+1]);
+    }
+
+    drawDigitChar (dest, 59, height - 14, digits[7]);
+
+    return baseWidth;
+}
+
+/* expand 8 UPC-E digits into a UPC-A number, storing into the given result
+ * array, or just store '\0' into the first element, if the form factor
+ * is incorrect; this will also calculate the check digit, if it is
+ * specified as '?' */
+void expandToUpcADigits (char *compressed, char *expanded)
+{
+    int i;
+
+    if ((compressed[0] != '0') && (compressed[0] != '1'))
+    {
+       return;
+    }
+
+    expanded[0] = compressed[0];
+    expanded[6] = '0';
+    expanded[7] = '0';
+    expanded[11] = compressed[7];
+
+    switch (compressed[6])
+    {
+       case '0':
+       case '1':
+       case '2':
+       {
+           expanded[1] = compressed[1];
+           expanded[2] = compressed[2];
+           expanded[3] = compressed[6];
+           expanded[4] = '0';
+           expanded[5] = '0';
+           expanded[8] = compressed[3];
+           expanded[9] = compressed[4];
+           expanded[10] = compressed[5];
+           break;
+       }
+       case '3':
+       {
+           expanded[1] = compressed[1];
+           expanded[2] = compressed[2];
+           expanded[3] = compressed[3];
+           expanded[4] = '0';
+           expanded[5] = '0';
+           expanded[8] = '0';
+           expanded[9] = compressed[4];
+           expanded[10] = compressed[5];
+           break;
+       }
+       case '4':
+       {
+           expanded[1] = compressed[1];
+           expanded[2] = compressed[2];
+           expanded[3] = compressed[3];
+           expanded[4] = compressed[4];
+           expanded[5] = '0';
+           expanded[8] = '0';
+           expanded[9] = '0';
+           expanded[10] = compressed[5];
+           break;
+       }
+       default:
+       {
+           expanded[1] = compressed[1];
+           expanded[2] = compressed[2];
+           expanded[3] = compressed[3];
+           expanded[4] = compressed[4];
+           expanded[5] = compressed[5];
+           expanded[8] = '0';
+           expanded[9] = '0';
+           expanded[10] = compressed[6];
+           break;
+       }
+    }
+
+    if (expanded[11] == '?')
+    {
+       unsigned int mul = 3;
+       unsigned int sum = 0;
+
+       for (i = 0; i < 11; i++)
+       {
+           sum += charToDigit (expanded[i]) * mul;
+           mul ^= 2;
+       }
+
+        expanded[11] = ((10 - (sum % 10)) % 10) + '0';
+    }
+}
+
+/* make and return a UPC-E barcode */
+int makeUpcE (Bitmap *dest, char *digits, int y)
+{
+    char expandedDigits[13];
+    char compressedDigits[9];
+
+    expandedDigits[0] = '\0';
+    compressedDigits[0] = '0';
+    strcpy (compressedDigits + 1, digits);
+
+    expandToUpcADigits (compressedDigits, expandedDigits);
+    if (expandedDigits[0] == '\0')
+    {
+       return 0;
+    }
+    
+    compressedDigits[7] = expandedDigits[11];
+
+    return makeUpcEFull (dest, compressedDigits, y);
+}
+
+/* draw the actual barcode part of a EAN-13 barcode */
+void drawEan13Bars (Bitmap *upcBitmap, char *digits, int x, int y, 
+                  int barY2, int guardY2)
+{
+    int i;
+    int leftPattern = ean13FirstDigit[charToDigit (digits[0])];
+
+    /* header */
+    bitmapVlin (upcBitmap, x, y, guardY2);
+    bitmapVlin (upcBitmap, x + 2, y, guardY2);
+
+    /* center marker */
+    bitmapVlin (upcBitmap, x + 46, y, guardY2);
+    bitmapVlin (upcBitmap, x + 48, y, guardY2);
+
+    /* trailer */
+    bitmapVlin (upcBitmap, x + 92, y, guardY2);
+    bitmapVlin (upcBitmap, x + 94, y, guardY2);
+
+    for (i = 0; i < 6; i++)
+    {
+       UpcSet lset = (leftPattern & (1 << (5 - i))) ? UPC_LEFT_B : UPC_LEFT_A;
+
+        drawUpcEanDigit (upcBitmap,
+                        x + 3 + i*7, 
+                        y,
+                        barY2,
+                        digits[i+1], 
+                        lset);
+        drawUpcEanDigit (upcBitmap,
+                        x + 50 + i*7, 
+                        y, 
+                        barY2,
+                        digits[i+7], 
+                        UPC_RIGHT);
+    }
+}
+
+/* make and return a full-height EAN-13 barcode */
+int makeEan13Full (Bitmap *dest, char *digits, int y)
+{
+    static int baseWidth = 102;
+    static int baseHeight = 60;
+
+    int height = baseHeight + y;
+    int i;
+
+    bitmapClear (dest);
+    drawEan13Bars (dest, digits, 6, y, height - 10, height - 4);
+
+    drawDigitChar (dest, 0, height - 7, digits[0]);
+
+    for (i = 0; i < 6; i++)
+    {
+        drawDigitChar (dest, 11 + i*7, height - 7, digits[i+1]);
+        drawDigitChar (dest, 57 + i*7, height - 7, digits[i+7]);
+    }
+
+    return baseWidth;
+}
+
+/* make and return an EAN-13 barcode */
+int makeEan13 (Bitmap *dest, char *digits, int y)
+{
+    int i;
+    unsigned int mul = 1;
+    unsigned int sum = 0;
+
+    for (i = 0; i < 12; i++)
+    {
+       sum += charToDigit (digits[i]) * mul;
+       mul ^= 2;
+    }
+
+    if (digits[12] == '?')
+    {
+        digits[12] = ((10 - (sum % 10)) % 10) + '0';
+    }
+
+    return makeEan13Full (dest, digits, y);
+}
+
+/* draw the actual barcode part of an EAN-8 barcode */
+void drawEan8Bars (Bitmap *upcBitmap, char *digits, int x, int y, 
+                  int barY2, int guardY2)
+{
+    int i;
+
+    /* header */
+    bitmapVlin (upcBitmap, x, y, guardY2);
+    bitmapVlin (upcBitmap, x + 2, y, guardY2);
+
+    /* center marker */
+    bitmapVlin (upcBitmap, x + 32, y, guardY2);
+    bitmapVlin (upcBitmap, x + 34, y, guardY2);
+
+    /* trailer */
+    bitmapVlin (upcBitmap, x + 64, y, guardY2);
+    bitmapVlin (upcBitmap, x + 66, y, guardY2);
+
+    for (i = 0; i < 4; i++)
+    {
+        drawUpcEanDigit (upcBitmap,
+                        x + 3 + i*7, 
+                        y,
+                        barY2,
+                        digits[i], 
+                        UPC_LEFT_A);
+        drawUpcEanDigit (upcBitmap,
+                        x + 36 + i*7, 
+                        y, 
+                        barY2,
+                        digits[i+4], 
+                        UPC_RIGHT);
+    }
+}
+
+/* make and return a full-height EAN-8 barcode */
+int makeEan8Full (Bitmap *dest, char *digits, int y)
+{
+    static int baseWidth = 68;
+    static int baseHeight = 60;
+
+    int height = baseHeight + y;
+    int i;
+
+    bitmapClear (dest);
+    drawEan8Bars (dest, digits, 0, y, height - 10, height - 4);
+
+    for (i = 0; i < 4; i++)
+    {
+        drawDigitChar (dest, 5 + i*7, height - 7, digits[i]);
+        drawDigitChar (dest, 37 + i*7, height - 7, digits[i+4]);
+    }
+
+    return baseWidth;
+}
+
+/* make and return an EAN-8 barcode */
+int makeEan8 (Bitmap *dest, char *digits, int y)
+{
+    int i;
+    unsigned int mul = 3;
+    unsigned int sum = 0;
+
+    for (i = 0; i < 7; i++)
+    {
+       sum += charToDigit (digits[i]) * mul;
+       mul ^= 2;
+    }
+
+    if (digits[7] == '?')
+    {
+        digits[7] = ((10 - (sum % 10)) % 10) + '0';
+    }
+
+    return makeEan8Full (dest, digits, y);
+}
+
+/* Dispatch to the right form factor UPC/EAN barcode generator */
+void processUpcEan (char *str, Bitmap *dest)
+{
+    char digits[16];
+    int digitCount = 0;
+    char supDigits[8];
+    int supDigitCount = 0;
+    char *instr = str;
+    char *banner = NULL; 
+    int supplement = 0;
+    int vstart = 9;
+    int width = 0;
+
+    while ((digitCount < 15) && (supDigitCount < 7))
+    {
+       char c = *instr;
+        if (((c >= '0') && (c <= '9')) || (c == '?'))
+        {
+           if (supplement)
+           {
+               supDigits[supDigitCount] = *instr;
+               supDigitCount++;
+           }
+           else
+           {
+               digits[digitCount] = *instr;
+               digitCount++;
+           }
+        }
+       else if (c == ',')
+       {
+           supplement = 1;
+       }
+       else if (c == ':')
+       {
+           banner = instr + 1;
+           break;
+       }
+       else if (c == '\0')
+       {
+           break;
+       }
+       instr++;
+    }
+
+    digits[digitCount] = '\0';
+    supDigits[supDigitCount] = '\0';
+
+    if (supDigitCount == 0)
+    {
+       supplement = 0;
+    }
+    else if ((supDigitCount == 2) || (supDigitCount == 5))
+    {
+       supplement = upcEanSupplementWidth (supDigits);
+    }
+    else
+    {
+       printf ("Invalid supplement (must be 2 or 5 digits)\n");
+       exit (1);
+    }
+
+    if (banner == NULL) 
+    {
+       banner = "barcode";
+    }
+
+    switch (digitCount)
+    {
+       case 7: 
+       {
+           width = makeUpcE (dest, digits, vstart);
+           break;
+       }
+       case 8: 
+       {
+           width = makeEan8 (dest, digits, vstart);
+           break;
+       }
+       case 12: 
+       {
+           width = makeUpcA (dest, digits, vstart);
+           break;
+       }
+       case 13:
+       {
+           width = makeEan13 (dest, digits, vstart);
+           break;
+       }
+       default:
+       {
+           printf("Bad barcode\n");
+           exit(1);
+       }
+    }
+
+    if (supplement)
+    {
+       drawUpcEanSupplementalBars (dest, supDigits,
+                                   width,
+                                   vstart + 1, dest->height - 4, 1);
+    }
+
+    if (banner != NULL)
+    {
+       bitmapDrawString5x8 (dest, 
+                            (width + supplement -
+                             ((int) strlen (banner) * 5)) / 2,
+                            0,
+                            banner);
+    }
+}
+
+
+
+/* ----------------------------------------------------------------------------
+ * the screenhack
+ */
+
+/*
+ * overall setup stuff
+ */
+
+/* set up the system */
+static void setup (void)
+{
+    XWindowAttributes xgwa;
+    XGCValues gcv;
+
+    XGetWindowAttributes (display, window, &xgwa);
+
+    screen = xgwa.screen;
+    visual = xgwa.visual;
+    cmap = xgwa.colormap;
+    windowWidth = xgwa.width;
+    windowHeight = xgwa.height;
+
+    gcv.background = get_pixel_resource ("background", "Background",
+                                        display, xgwa.colormap);
+    gcv.foreground = get_pixel_resource ("foreground", "Foreground",
+                                        display, xgwa.colormap);
+    theGC = XCreateGC (display, window, GCForeground|GCBackground, &gcv);
+
+    theBitmap = makeBitmap(BARCODE_WIDTH * MAX_MAG, BARCODE_HEIGHT * MAX_MAG);
+    theImage = XCreateImage(display, visual, 1, XYBitmap, 0, theBitmap->buf,
+                           theBitmap->width, theBitmap->height, 8,
+                           theBitmap->widthBytes);
+}
+
+
+
+/*
+ * the simulation
+ */
+
+/* set up the model */
+static void setupModel (void)
+{
+    int i;
+
+    barcode_max = 20;
+    barcode_count = 0;
+    barcodes = malloc (sizeof (Barcode) * barcode_max);
+
+    for (i = 0; i < barcode_max; i++)
+    {
+       barcodes[i].bitmap = makeBitmap(BARCODE_WIDTH * MAX_MAG, 
+                                       BARCODE_HEIGHT * MAX_MAG);
+    }
+}
+
+/* make a new barcode string */
+static void makeBarcodeString (char *str)
+{
+    int dig, i;
+
+    switch ((int) (RAND_FLOAT_01 * 4))
+    {
+       case 0:  dig = 6;  break;
+       case 1:  dig = 7;  break;
+       case 2:  dig = 11; break;
+       default: dig = 12; break;
+    }
+
+    for (i = 0; i < dig; i++)
+    {
+       str[i] = RAND_FLOAT_01 * 10 + '0';
+    }
+
+    str[i] = '?';
+    i++;
+
+    switch ((int) (RAND_FLOAT_01 * 3))
+    {
+       case 0:  dig = 0; break;
+       case 1:  dig = 2; break;
+       default: dig = 5; break;
+    }
+
+    if (dig != 0)
+    {
+       str[i] = ',';
+       i++;
+       while (dig > 0) 
+       {
+           str[i] = RAND_FLOAT_01 * 10 + '0';
+           i++;
+           dig--;
+       }
+    }
+
+    str[i] = ':';
+    i++;
+
+    strcpy(&str[i], words[(int) (RAND_FLOAT_01 * WORD_COUNT)]);
+}
+
+/* update the model for one iteration */
+static void scrollModel (void)
+{
+    int i;
+
+    for (i = 0; i < barcode_count; i++) 
+    {
+       Barcode *b = &barcodes[i];
+       b->x--;
+       if ((b->x + BARCODE_WIDTH * b->mag) < 0) 
+       {
+           /* fell off the edge */
+           if (i != (barcode_count - 1)) {
+               Bitmap *oldb = b->bitmap;
+               memmove (b, b + 1, (barcode_count - i - 1) * sizeof (Barcode));
+               barcodes[barcode_count - 1].bitmap = oldb;
+
+                XFreeColors (display, cmap, &b->pixel, 1, 0);
+           }
+
+           i--;
+           barcode_count--;
+       }
+    }
+
+    while (barcode_count < barcode_max)
+    {
+       Barcode *barcode = &barcodes[barcode_count];
+       barcode->x = (barcode_count == 0) ? 
+           0 : 
+           (barcodes[barcode_count - 1].x + 
+            barcodes[barcode_count - 1].mag * BARCODE_WIDTH);
+       barcode->x += RAND_FLOAT_01 * 100;
+       barcode->mag = RAND_FLOAT_01 * MAX_MAG;
+       barcode->y =
+           RAND_FLOAT_01 * (windowHeight - BARCODE_HEIGHT * barcode->mag);
+       if (barcode->y < 0) 
+       {
+           barcode->y = 0;
+       }
+       makeBarcodeString(barcode->code);
+       processUpcEan (barcode->code, theBitmap);
+       bitmapScale (barcode->bitmap, theBitmap, barcode->mag);
+
+        {
+          XColor c;
+          int i, ok = 0;
+          for (i = 0; i < 100; i++)
+            {
+              hsv_to_rgb (random() % 360, 1.0, 1.0, &c.red, &c.green, &c.blue);
+              ok = XAllocColor (display, cmap, &c);
+              if (ok) break;
+            }
+          if (!ok)
+            {
+              c.red = c.green = c.blue = 0xFFFF;
+              if (!XAllocColor (display, cmap, &c))
+                abort();
+            }
+          barcode->pixel = c.pixel;
+        }
+
+       barcode_count++;
+    }
+}
+
+/* update the model for one iteration */
+static void updateGrid (void)
+{
+    int i, x, y;
+    static int grid_w = 0;
+    static int grid_h = 0;
+
+    static unsigned long pixel;
+    static int alloced_p = 0;
+
+    static char *strings[200] = { 0, };
+
+    if (grid_w == 0 || grid_h == 0 ||
+        (! (random() % 400)))
+      {
+        XClearWindow (display, window);
+        grid_w = 1 + (random() % 3);
+        grid_h = 1 + (random() % 4);
+      }
+
+    if (!alloced_p || (! (random() % 100)))
+      {
+        XColor c;
+        hsv_to_rgb (random() % 360, 1.0, 1.0, &c.red, &c.green, &c.blue);
+        if (alloced_p)
+          XFreeColors (display, cmap, &pixel, 1, 0);
+        XAllocColor (display, cmap, &c);
+        pixel = c.pixel;
+        alloced_p = 1;
+      }
+
+    barcode_count = grid_w * grid_h;
+    if (barcode_count > barcode_max) abort();
+
+    for (i = 0; i < barcode_max; i++)
+      {
+        Barcode *b = &barcodes[i];
+        b->x = b->y = 999999;
+      }
+
+    i = 0;
+    for (y = 0; y < grid_h; y++)
+      for (x = 0; x < grid_w; x++, i++)
+        {
+          Barcode *b = &barcodes[i];
+          int digits = 12;
+
+          int cell_w = (windowWidth  / grid_w);
+          int cell_h = (windowHeight / grid_h);
+          int mag_x  = cell_w / BARCODE_WIDTH;
+          int mag_y  = cell_h / BARCODE_HEIGHT;
+          int BW = 108 /*BARCODE_WIDTH*/;
+          int BH = BARCODE_HEIGHT;
+
+          b->mag = (mag_x < mag_y ? mag_x : mag_y);
+
+          b->x = (x * cell_w) + ((cell_w - b->mag * BW) / 2);
+          b->y = (y * cell_h) + ((cell_h - b->mag * BH) / 2);
+          b->pixel = pixel;
+
+          if (!strings[i])
+            {
+              int j;
+              char *s = malloc (digits + 10);
+              strings[i] = s;
+              for (j = 0; j < digits; j++)
+                s[j] = (random() % 10) + '0';
+              s[j++] = '?';
+              s[j++] = ':';
+              s[j++] = 0;
+            }
+
+          /* change one digit in this barcode */
+          strings[i][random() % digits] = (random() % 10) + '0';
+
+          strcpy (b->code, strings[i]);
+          processUpcEan (b->code, b->bitmap);
+        }
+}
+
+
+
+/* render and display the current model */
+static void renderFrame (void)
+{
+    int i;
+
+    for (i = 0; i < barcode_count; i++)
+    {
+       Barcode *barcode = &barcodes[i];
+
+       if (barcode->x > windowWidth) {
+           break;
+       }
+
+       /* bitmapScale (theBitmap, barcode->bitmap, barcode->mag);*/
+       theImage->data = barcode->bitmap->buf;
+
+        XSetForeground (display, theGC, barcode->pixel);
+       XPutImage (display, window, theGC, theImage, 
+                  0, 0, barcode->x, barcode->y, 
+                  BARCODE_WIDTH * barcode->mag,
+                  BARCODE_HEIGHT * barcode->mag);
+    }
+}
+
+/* do one iteration */
+static void oneIteration (void)
+{
+    if (scroll_p)
+      scrollModel ();
+    else
+      updateGrid ();
+    renderFrame ();
+}
+
+
+
+/* main and options and stuff */
+
+char *progclass = "Barcode";
+
+char *defaults [] = {
+    ".background:      black",
+    ".foreground:      white",
+    "*delay:           10000",
+    0
+};
+
+XrmOptionDescRec options [] = {
+  { "-delay",            ".delay",          XrmoptionSepArg, 0 },
+  { 0, 0, 0, 0 }
+};
+
+/* initialize the user-specifiable params */
+static void initParams (void)
+{
+    int problems = 0;
+
+    delay = get_integer_resource ("delay", "Delay");
+    if (delay < 0)
+    {
+       fprintf (stderr, "error: delay must be at least 0\n");
+       problems = 1;
+    }
+
+    scroll_p = 1;
+
+    if (problems)
+    {
+       exit (1);
+    }
+}
+
+/* main function */
+void screenhack (Display *dpy, Window win)
+{
+    display = dpy;
+    window = win;
+
+    initParams ();
+    setup ();
+    setupModel ();
+
+    for (;;) 
+    {
+       oneIteration ();
+        XSync (dpy, False);
+        screenhack_handle_events (dpy);
+       usleep (delay);
+    }
+}