Here is my solution as promised:
first of all i used gdi32.dll, so lets stick this piece of code in somewhere...
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll"

]
private static extern bool BitBlt(
IntPtr hdcDest, // handle to destination DC
int nXDest, // x-coord of destination upper-left corner
int nYDest, // y-coord of destination upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
IntPtr hdcSrc, // handle to source DC
int nXSrc, // x-coordinate of source upper-left corner
int nYSrc, // y-coordinate of source upper-left corner
System.Int32 dwRop // raster operation code
);
...i can use BitBlt to copy from a Graphics object to a Bitmap. hurrah, now i can use DrawString. now i need a graphics object that can be any size i choose, and that won't be drawn on screen...
Bitmap bm = new Bitmap(p.pageWidth, p.pageHeight);
IntPtr hbm = bm.GetHbitmap();
bob = Image.FromHbitmap(hbm);
Graphics g = Graphics.FromImage(bob);
...there's probably a better way of getting this graphics object, but i'm gonna use hbm again soon so i think that justifies it. the next step is to diddle with the graphics object - i drew a string. to finish with, i have to copy Graphics g to bob...
IntPtr hdc = g.GetHdc();
BitBlt(hbm, 0, 0, bob.Width, bob.Height, hdc, 0, 0, 13369376);
g.ReleaseHdc(hdc);
...and that's it. spanky. incedentaly, bob is short for 'Blitter OBject', another term for 'raster garphic'.
i think the routine is pretty tight, but i'd like to hear people ideas for bettering it.