Development: Examples

1. Masks for images (PDF 1.3)
Q: PDF 1.3 has documented a new "Mask" key that provides explicit masking ability. This is supposed to allow you to embed an image (say, a jpeg) along with a 1 bit-per-sample mask that indicates where the jpeg should and should not appear on the page.
2. ASCII85 encoding / decoding
Q: I'm looking for a piece of code to do ASCII85 encoding/decoding. Does anyone know where to get this? [Q6]
3. Where can I find samples of xxx?
Q: I need to obtain samples of streams that are encoded with each of the types of filters that PDF 1.3 supports. Is there a "test bank" anywhere? Ideally, the streams would be fairly small to facilitate debugging. Some of them, like FlateDecode and CCITTFaxDecode are easy to find. But for example, I haven't been able to locate an ASCIIHexDecode stream anywhere. [Q11]
4. Annotations and appearance streams
Q: In section 7.4.4 of the PDF spec 1.3, I read about appearance streams. As far, I only found appearance streams used in conjunction with annotations of the subtype /Widget. The spec doesn't mention that the use of appearance streams (/AP) is restricted to the subtype /Widget, so I wanted to ask if it is possible to give an appearance to an annotation of the subtype /Link ? [Q166]
5. Masks for documents
Q: Is it possible to set the background of a PDF to alpha channel? [Q174]

1. Masks for images (PDF 1.3)

Q: PDF 1.3 has documented a new "Mask" key that provides explicit masking ability. This is supposed to allow you to embed an image (say, a jpeg) along with a 1 bit-per-sample mask that indicates where the jpeg should and should not appear on the page.

I haven't been able to get this to work -- the image just renders without respecting the mask.

Does anyone have any experience with this feature? The PDF reference does not have any sample code that does this, so I would be interested in any examples you could throw my way. [Q4]

A: It would be easier if you provided an excerpt of the PDF you generated. Don't forget to put the ImageMask dictionary entry in the mask image, the mask's BitsPerComponent to 1, and no color space.

Pierre Baillargeon (pb@artquest.net) [A9]

A: And another thing I noticed about legal masks is that the mask image XObject appears in the resource dictionary for the page it appears on.

Ed Bomke (edb@discryptic.com) [A10]

A: Here are some fragments from one that works:

 
25 0 obj << 
/Type /XObject 
/Subtype /Image 
/Width 317 
/Height 299 
/BitsPerComponent 1 
/ImageMask true 
/Length 524 
/Filter /CCITTFaxDecode 
/DecodeParms << 
/K -1 
/Columns 317 
>> >> 
stream 

26 0 obj << 
/Type /XObject 
/Subtype /Image 
/Mask 25 0 R 
/Width 317 
/Height 299 
/BitsPerComponent 8 
/ColorSpace /DeviceRGB 
/Length 95613 
/Filter /DCTDecode 
>> 
stream 
 

Aandi Inston (quite@dial.pipex.com) [A11]

2. ASCII85 encoding / decoding

Q: I'm looking for a piece of code to do ASCII85 encoding/decoding. Does anyone know where to get this? [Q6]

A: ftp://ftp.webcom.com/pub/haahr/src/encode85.c

ftp://ftp.webcom.com/pub/haahr/src/decode85.c

Tom Kacvinsky (tjk@ams.org) [A15]

A: The text of the file ftp://ftp.webcom.com/pub/haahr/src/encode85.c:

 
/* encode85 -- convert to ascii85 format */

#include <stdio.h>
#define atoi(s) strtol(s, 0, 0)

static unsigned long width = 72, pos = 0, tuple = 0;
static int count = 0;

void init85(void) {
        printf("<~");
        pos = 2;
}

void encode(unsigned long tuple, int count) {
        int i;
        char buf[5], *s = buf;
        i = 5;
        do {
                *s++ = tuple % 85;
                tuple /= 85;
        } while (--i > 0);
        i = count;
        do {
                putchar(*--s + '!');
                if (pos++ >= width) {
                        pos = 0;
                        putchar('
');
                }
        } while (i-- > 0);
}

void put85(unsigned c) {
        switch (count++) {
        case 0: tuple |= (c << 24); break;
        case 1: tuple |= (c << 16); break;
        case 2: tuple |= (c <<  8); break;
        case 3:
                tuple |= c;
                if (tuple == 0) {
                        putchar('z');
                        if (pos++ >= width) {
                                pos = 0;
                                putchar('
');
                        }
                } else
                        encode(tuple, count);
                tuple = 0;
                count = 0;
                break;
        }
}

void cleanup85(void) {
        if (count > 0)
                encode(tuple, count);
        if (pos + 2 > width)
                putchar('
');
        printf("~>
");
}

void copy85(FILE *fp) {
        unsigned c;
        while ((c = getc(fp)) != EOF)
                put85(c);
}

void usage(void) {
        fprintf(stderr, "usage: encode85 [-w width] file ...
");
        exit(1);
}

extern int getopt(int, char *[], const char *);
extern int optind;
extern char *optarg;

int main(int argc, char *argv[]) {
        int i;
        while ((i = getopt(argc, argv, "w:?")) != EOF)
                switch (i) {
                case 'w':
                        width = atoi(optarg);
                        if (width == 0)
                                width = ~0;
                        break;
                case '?':
                        usage();
                }
        
        init85();
        if (optind == argc)
                copy85(stdin);
        else
                for (i = optind; i < argc; i++) {
                        FILE *fp = fopen(argv[i], "r");
                        if (fp == NULL) {
                                perror(argv[i]);
                                return 1;
                        }
                        copy85(fp);
                        fclose(fp);
                }
        cleanup85();
        return 0;
}
 

Michael Still (mikal@stillhq.com) [A246]

A: The text of the file ftp://ftp.webcom.com/pub/haahr/src/decode85.c:

 
/* decode85 -- convert from ascii85 format */

#include <stdio.h>

static unsigned long pow85[] = {
        85*85*85*85, 85*85*85, 85*85, 85, 1
};

void wput(unsigned long tuple, int bytes) {
        switch (bytes) {
        case 4:
                putchar(tuple >> 24);
                putchar(tuple >> 16);
                putchar(tuple >>  8);
                putchar(tuple);
                break;
        case 3:
                putchar(tuple >> 24);
                putchar(tuple >> 16);
                putchar(tuple >>  8);
                break;
        case 2:
                putchar(tuple >> 24);
                putchar(tuple >> 16);
                break;
        case 1:
                putchar(tuple >> 24);
                break;
        }
}

void decode85(FILE *fp, const char *file) {
        unsigned long tuple = 0;
        int c, count = 0;
        for (;;)
                switch (c = getc(fp)) {
                default:
                        if (c < '!' || c > 'u') {
                                fprintf(stderr, "%s: bad character in ascii85 region: %#o
", file, c);
                                exit(1);
                        }
                        tuple += (c - '!') * pow85[count++];
                        if (count == 5) {
                                wput(tuple, 4);
                                count = 0;
                                tuple = 0;
                        }
                        break;
                case 'z':
                        if (count != 0) {
                                fprintf(stderr, "%s: z inside ascii85 5-tuple
", file);
                                exit(1);
                        }
                        putchar(0);
                        putchar(0);
                        putchar(0);
                        putchar(0);
                        break;
                case '~':
                        if (getc(fp) == '>') {
                                if (count > 0) {
                                        count--;
                                        tuple += pow85[count];
                                        wput(tuple, count);
                                }
                                c = getc(fp);
                                return;
                        }
                        fprintf(stderr, "%s: ~ without > in ascii85 section
", file);
                        exit(1);
                case '
': case '
': case '	': case ' ':
                case '': case 'f': case '': case 0177:
                        break;
                case EOF:
                        fprintf(stderr, "%s: EOF inside ascii85 section
", file);
                        exit(1);
                }
}

void decode(FILE *fp, const char *file, int preserve) {
        int c;
        while ((c = getc(fp)) != EOF)
                if (c == '<')
                        if ((c = getc(fp)) == '~')
                                decode85(fp, file);
                        else {
                                if (preserve)
                                        putchar('<');
                                if (c == EOF)
                                        break;
                                if (preserve)
                                        putchar(c);
                        }
                else
                        if (preserve)
                                putchar(c);
}

void usage(void) {
        fprintf(stderr, "usage: decode85 [-p] file ...
");
        exit(1);
}

extern int getopt(int, char *[], const char *);
extern int optind;
extern char *optarg;

int main(int argc, char *argv[]) {
        int i, preserve;
        preserve = 0;
        while ((i = getopt(argc, argv, "p?")) != EOF)
                switch (i) {
                case 'p': preserve = 1; break;
                case '?': usage();
                }
        

        if (optind == argc)
                decode(stdin, "decode85", preserve);
        else
                for (i = optind; i < argc; i++) {
                        FILE *fp = fopen(argv[i], "r");
                        if (fp == NULL) {
                                perror(argv[i]);
                                return 1;
                        }
                        decode(fp, argv[i], preserve);
                        fclose(fp);
                }
        return 0;
}
 

Michael Still (mikal@stillhq.com) [A247]

3. Where can I find samples of xxx?

Q: I need to obtain samples of streams that are encoded with each of the types of filters that PDF 1.3 supports. Is there a "test bank" anywhere? Ideally, the streams would be fairly small to facilitate debugging. Some of them, like FlateDecode and CCITTFaxDecode are easy to find. But for example, I haven't been able to locate an ASCIIHexDecode stream anywhere. [Q11]

A: Have you tried the spec? There is an examples appendix, as well as many examples spread throughout the document. I can't check your specific example for you, as my copy is at work, but you should be fine.

Michael Still (mikal@stillhq.com) [A20]

4. Annotations and appearance streams

Q: In section 7.4.4 of the PDF spec 1.3, I read about appearance streams. As far, I only found appearance streams used in conjunction with annotations of the subtype /Widget. The spec doesn't mention that the use of appearance streams (/AP) is restricted to the subtype /Widget, so I wanted to ask if it is possible to give an appearance to an annotation of the subtype /Link ? [Q166]

A: Starting with Acrobat 4.0, you will find that most annotation types also generate appearance streams, so that the annotation can print, and be displayed in Reader (which lacks handlers for most annotation types, though not all). Link, however, is not included. It is an interesting question whether it can have an appearance stream. I see a problem, since if the annotation handler is installed, it is used to draw the annotation instead of the appearance stream. The appearance stream must therefore match the annotation's own idea of what it looks like, so setting different AS values is only really practical for your own annotation classes, not the standard ones.

Aandi Inston (quite@dial.pipex.com) [A253]

5. Masks for documents

Q: Is it possible to set the background of a PDF to alpha channel? [Q174]

A: In theory, in Acrobat 5.0, yes. In practice, few tools could do this; Illustrator could. Be aware that you would be dooming the user to particularly slow printing.

Aandi Inston (quite@dial.pipex.com) [A264]