5
\$\begingroup\$

We've got the basic functionality of using my postscript interpreter, xpost, as a library. I'd like to solicit some feedback from the community on the API setup. This example program, called xpost_client.c illustrates the API to rasterize a small EPS into a 792x612 BGR buffer, which it then dumps to a file as an ASCII PPM file (so you can check it with a text editor).

/* 
   This is a simple example of a client calling xpost as a library 
   with a postscript program, desiring the raster data of the 
   generated image. 

TODO: 
    define buffer interchange type 
 */ 

#include <stdlib.h> 
#include <stdio.h> 

#include "xpost.h" 
#include "xpost_memory.h" 
#include "xpost_object.h" 
#include "xpost_context.h" 
#include "xpost_interpreter.h" 

char *prog = 
    "%%BoundingBox: 200 300 400 500\n" 
    "300 400 100 0 360 arc\n" 
    "fill\n" 
    "showpage\n"; 

int main() { 
    void *buffer_type_object; 
    xpost_init(); 
    xpost_create("bgr", 
            XPOST_OUTPUT_BUFFEROUT, 
            &buffer_type_object, 
            XPOST_SHOWPAGE_RETURN, 
            1); 
    xpost_run(XPOST_INPUT_STRING, prog); 
    { 
        unsigned char *buffer = buffer_type_object; 
        int i,j; 
        FILE *fp = fopen("xpost_client_out.ppm", "w"); 
        fprintf(fp, "P3\n612 792\n255\n"); 
        for (i=0; i<792; i++) { 
            for (j=0; j<612; j++) { 
                unsigned int red, green, blue; 
                red = *buffer++; 
                green = *buffer++; 
                blue = *buffer++; 
                ++buffer; 
                fprintf(fp, "%d ", red); 
                fprintf(fp, "%d ", green); 
                fprintf(fp, "%d ", blue); 
                if ((j%20)==0) 
                    fprintf(fp, "\n"); 
            } 
            fprintf(fp, "\n"); 
        } 
        fclose(fp); 
    } 
    xpost_destroy(); 
    free(buffer_type_object); 
    xpost_quit(); 
    return 0;
} 

Currently only the bgr device offers the OUTPUT_BUFFEROUT option. The C code assumes that the PS program contains a showpage to trigger the copying of the buffer. No showpage :: your buffer's a trash-pointer, dude.

I do plan to sweep-up the auxiliary headers into a single "xpost.h" header file. But that's the full list you currently need from the top-level.

One obvious flaw is that there's currently no way to set the geometry or resolution. It's 1-pt per pixel, and that's that. It's also currently hardcoded at letter dimensions, (sorry, rest of the world). Does anybody have ideas about the best way to interface for that at the top level?

Any other options that a library user might want? Does the buffer-type struct need anything more than height, width and stride? Should stride count words or bytes?

In unixes, Cygwin and mingw, you can

hg clone https://luser.droog@code.google.com/p/xpost/ 

then read the INSTALL stuff, but the quick version is

./configure 
make 
sudo make install 

then you can run

xpost_client 

to produce a (buggy) rendering of a filled circle in xpost_client_out.ppm which loads ok for me in Gimp.

The program can also be browsed online at:

http://code.google.com/p/xpost/source/browse/src/bin/xpost_client.c
http://code.google.com/p/xpost/source/browse/src/lib/xpost.h
http://code.google.com/p/xpost/source/browse/src/lib/xpost_memory.h
http://code.google.com/p/xpost/source/browse/src/lib/xpost_object.h http://code.google.com/p/xpost/source/browse/src/lib/xpost_context.h
http://code.google.com/p/xpost/source/browse/src/lib/xpost_interpreter.h

Note: I have also posted this code to comp.lang.postscript for a more discussion-type discussion.

Also note: The interpreter produces temporary files in the current directory named gmemXXXXXX lmemXXXXXX xdumpXXXXXX (where XXXXXX is a system-generated unique filename sequence) which may needlessly accumulate diskspace. They periodically must be manually removed. And be careful with code that may do an infinite series of pushes to the stack, lmemXXXXXX may grow very large to hold a huge stack.

\$\endgroup\$
  • \$\begingroup\$ I think that this is a well crafted question, with sufficient reviewable code included, but still short enough to be reasonably limited in scope. The full library code hosted on Google Code is useful supplementary information. \$\endgroup\$ – 200_success Oct 8 '14 at 7:47
3
\$\begingroup\$

Based on these two lines…

xpost_create("bgr", 
        XPOST_OUTPUT_BUFFEROUT, 
        &buffer_type_object, 
        XPOST_SHOWPAGE_RETURN, 
        1); 
xpost_run(XPOST_INPUT_STRING, prog);

I am puzzled by the interaction between xpost_create() and xpost_run(). Is it possible to create and use multiple xpost contexts at once? If so, shouldn't xpost_create() return some kind of handle that would be passed as a parameter to xpost_run()? Or, if not, then why should I need to call xpost_create() and xpost_destroy() at all?

I would expect the library to behave more like a printer, in that it would produce an image for every showpage command. One way to make that work would be to register a callback, which would be called whenever showpage is encountered. Perhaps it could pass some information about the bounding box as parameters to the callback, since it's unreasonable to have to guess or hard-code that information.

It's odd that the color model is called "bgr", but the pixel values are arranged in the opposite order (red, green, blue) in the output buffer. I would also consider treating the buffer as an array of

typedef {
    int red;
    int green;
    int blue;
} struct pixel;

… instead of separate red, green, and blue integers.

Your example client doesn't seem to demonstrate any error-handling capability. What happens, for example, if the PostScript interpreter has a stack underflow?

\$\endgroup\$
  • \$\begingroup\$ Yes, it can run multiple contexts with cooperative scheduling to support Display Postscript. Good point about the error handling. Oops. That's a bug in the pixel-ordering. Thanks. I get so confused with the dang buffers. \$\endgroup\$ – luser droog Oct 8 '14 at 7:41
  • \$\begingroup\$ Also the same context can be re-used sequentially to factor-out the initialization in a batch job. \$\endgroup\$ – luser droog Oct 8 '14 at 7:54
  • \$\begingroup\$ It currently uses a (shudder) global variable to hold the context-pointer created by create and used by run. So this definitely should be teased-out as a handle. Good catch. \$\endgroup\$ – luser droog Oct 8 '14 at 8:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.