From 7b84391212adfa3b7f9fc5da1d3a3a5861023236 Mon Sep 17 00:00:00 2001 From: Fritz Elfert Date: Sun, 3 Mar 2002 23:55:35 +0000 Subject: - Added first version of PostScript prolog - Implemented page conversion (incomplete). --- plpprint/.cvsignore | 1 + plpprint/Makefile.am | 4 +- plpprint/plpprintd.cc | 571 +++++++++++++++++++++++++++++++++++++++++++++++--- plpprint/prolog.ps.in | 159 ++++++++++++++ 4 files changed, 702 insertions(+), 33 deletions(-) create mode 100644 plpprint/prolog.ps.in (limited to 'plpprint') diff --git a/plpprint/.cvsignore b/plpprint/.cvsignore index 067f1e1..c5361f5 100644 --- a/plpprint/.cvsignore +++ b/plpprint/.cvsignore @@ -3,3 +3,4 @@ Makefile .libs .deps plpprintd +prolog.ps diff --git a/plpprint/Makefile.am b/plpprint/Makefile.am index f0d0a4e..9d44b15 100644 --- a/plpprint/Makefile.am +++ b/plpprint/Makefile.am @@ -6,5 +6,7 @@ sbin_PROGRAMS = plpprintd plpprintd_LDADD = $(top_srcdir)/lib/libplp.la plpprintd_SOURCES = plpprintd.cc +pkgdata_DATA = prolog.ps + maintainer-clean-local: - rm -f Makefile.in + rm -f Makefile.in prolog.ps diff --git a/plpprint/plpprintd.cc b/plpprint/plpprintd.cc index 7cf4006..9f32c22 100644 --- a/plpprint/plpprintd.cc +++ b/plpprint/plpprintd.cc @@ -36,6 +36,7 @@ #include #include +#include #define _GNU_SOURCE #include @@ -113,58 +114,564 @@ infolog(char *fmt, ...) return 0; } +static int minx, maxx, miny, maxy; +string usedfonts; + +typedef struct { + char *psifont; + bool bold; + bool italic; + char *psfont; +} fontmap_entry; + +#define FALLBACK_FONT "Courier" + +static fontmap_entry fontmap[] = { + { "Times New Roman", false, false, "Times-Roman"}, + { "Times New Roman", true, false, "Times-Bold"}, + { "Times New Roman", false, true, "Times-Italic"}, + { "Times New Roman", true, true, "Times-BoldItalic"}, + { "Arial", false, false, "Helvetica"}, + { "Arial", true, false, "Helvetica-Bold"}, + { "Arial", false, true, "Helvetica-Oblique"}, + { "Arial", true, true, "Helvetica-BoldOblique"}, + { "Courier New", false, false, "Courier"}, + { "Courier New", true, false, "Courier-Bold"}, + { "Courier New", false, true, "Courier-Oblique"}, + { "Courier New", true, true, "Courier-BoldOblique"}, + { "Swiss", false, false, "Courier"}, + { "Swiss", true, false, "Courier-Bold"}, + { "Swiss", false, true, "Courier-Oblique"}, + { "Swiss", true, true, "Courier-BoldOblique"}, + { NULL, false, false, NULL} +}; + +static void +ps_setfont(FILE *f, const char *fname, bool bold, bool italic, + unsigned long fsize) +{ + fontmap_entry *fe = fontmap; + char *psf = NULL; + while (fe->psifont) { + if ((!strcmp(fe->psifont, fname)) && + (fe->bold == bold) && + (fe->italic == italic)) { + psf = fe->psfont; + break; + } + fe++; + } + if (!psf) { + psf = FALLBACK_FONT; + errorlog("No font mapping for '%s' (%s%s%s); fallback to %s\n", + fname, (bold) ? "Bold" : "", (italic) ? "Italic" : "", + (bold || italic) ? "" : "Regular", psf); + } + if (usedfonts.find(psf) == usedfonts.npos) { + usedfonts += "%%+ font "; + usedfonts += psf; + usedfonts += "\n"; + } + fprintf(f, "%d /%s F\n", fsize, psf); +} + static void -convert_job(const char *jobname) +ps_escape(string &text) { - // ... To be done ... - unlink(jobname); + int pos = 0; + while ((pos = text.find_first_of("()", pos)) != text.npos) { + text.insert(pos, "\\"); + pos += 2; + } +} + +static void +ps_bitmap(FILE *f, int llx, int lly, int urx, int ury, const char *buf) +{ + bufferStore out; + int width, height; + if (decodeBitmap((const unsigned char *)buf, width, height, out)) { + fprintf(f, "%d %d %d %d I\n"); + const char *p = out.getString(0); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + fprintf(f, "%02x", *p++); + fprintf(f, "\n"); + } + } else + errorlog("Corrupted bitmap data"); +} + +static void +convertPage(FILE *f, int page, bool last, bufferStore buf) +{ + int len = buf.getLen(); + int i = 0; + long boffset = 0; + unsigned long left = 0; + unsigned long top = 0; + unsigned long right = 0; + unsigned long bottom = 0; + int lmargin = -1; + +#ifdef DEBUG + char dumpname[128]; + sprintf(dumpname, "/tmp/pdump_%d", page); + FILE *df = fopen(dumpname, "w"); + fwrite(buf.getString(0), 1, len, df); + fclose(df); + debuglog("Saved page input to %s\n", dumpname); +#endif + if (page == 0) { + time_t now = time(NULL); + fputs( + "%!PS-Adobe-3.0\n" + "%%Creator: plpprintd " VERSION "\n" + "%%CreationDate: ", f); + fputs(ctime(&now), f); + fputs( + "%%Pages: (atend)\n" + "%%BoundingBox: (atend)\n" + "%%DocumentNeededResources: (atend)\n" + "%%LanguageLvel: 2\n" + "%%EndComments\n" + "%%BeginProlog\n", f); + char pbuf[1024]; + FILE *pf = fopen(PKGDATA "/prolog.ps", "r"); + while (fgets(pbuf, sizeof(pbuf), pf)) + fputs(pbuf, f); + fclose(pf); + fputs( + "%%EndProlog\n" + "%%BeginSetup\n" + "currentpagedevice /PageSize get 1 get /top exch def\n" + "%%EndSetup\n", f); + minx = miny = 9999; + maxx = maxy = 0; + usedfonts = ""; + } + fprintf(f, "%%%%Page: %d %d\n", page+1, page+1); + while (i < len) { + unsigned char opcode = buf.getByte(i); + switch (opcode) { + case 0x00: { + // Start of section + unsigned long section = buf.getDWord(i+1); + fprintf(f, "%% @%d: Section %d\n", i, section); + // (section & 3) = + // 0 = Header, 1 = Body, 2 = Footer, 3 = Footer + i += 5; + } + break; + case 0x01: { + // End of page + i = len + 1; + } + break; + case 0x03: { + // ??? + fprintf(f, "%% @%d: U03 %d\n", i, buf.getByte(i+1)); + debuglog("@%d: U03 %d", i, buf.getByte(i+1)); + i += 2; + } + break; + case 0x04: { + // Bounding box + left = buf.getDWord(i+1); + top = buf.getDWord(i+5); + right = buf.getDWord(i+9); + bottom = buf.getDWord(i+13); + if (lmargin == -1) + lmargin = left; + if (left < minx) + minx = left; + if (right > maxx) + maxx = right; + if (top < miny) + miny = top; + if (bottom > maxy) + maxy = bottom; + i += 17; + fprintf(f, "%% @%d: bbox %d %d %d %d\n", i, left, top, right, + bottom); + } + break; + case 0x05: { + // ??? + fprintf(f, "%% @%d: U05\n", i); + debuglog("@%d: U05", i); + i++; + } + break; + case 0x06: { + // ??? + fprintf(f, "%% @%d: U06 %d 0x%08x\n", i, + buf.getByte(i+1), buf.getDWord(i+2)); + debuglog("@%d: U06 %d 0x%08x", i, + buf.getByte(i+1), buf.getDWord(i+2)); + i += 6; + } + break; + case 0x07: { + // Font + int namelen; + int ofs; + if (buf.getByte(i+1) & 1) { + namelen = buf.getWord(i+1) >> 3; + ofs = i + 3; + } else { + namelen = buf.getByte(i+1) >> 2; + ofs = i + 2; + } + string fname(buf.getString(ofs), namelen); + ofs += namelen; + int screenfont = buf.getByte(ofs); + int basesize = buf.getWord(ofs+1); + unsigned long style = buf.getDWord(ofs+3); + bool italic = ((style & 1) != 0); + bool bold = ((style & 2) != 0); + unsigned long fontsize = buf.getDWord(ofs+7); + boffset = (long)buf.getDWord(ofs+11); + fprintf(f, "%% @%d: Font '%s' %d %s%s%s\n", i, fname.c_str(), + fontsize, bold ? "Bold" : "", italic ? "Italic" : "", + (bold || italic) ? "" : "Regular"); + ps_setfont(f, fname.c_str(), bold, italic, fontsize); + i = ofs + 15; + } + break; + case 0x08: { + // ??? + fprintf(f, "%% @%d: U08\n", i); + debuglog("@%d: U08", i); + i++; + } + break; + case 0x09: { + // underline + fprintf(f, "%% @%d: Underline %d\n", i, buf.getByte(i+1)); + fprintf(f, "%d UL\n", buf.getByte(i+1)); + i += 2; + } + break; + case 0x0a: { + // strikethru + fprintf(f, "%% @%d: Strikethru %d\n", i, buf.getByte(i+1)); + fprintf(f, "%d ST\n", buf.getByte(i+1)); + i += 2; + } + break; + case 0x0b: { + // newline + fprintf(f, "%% @%d: Newline %d %d\n", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + i += 9; + } + break; + case 0x0c: { + // cr + fprintf(f, "%% @%d: CR %d %d\n", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + i += 9; + } + break; + case 0x0d: { + // foreground color + fprintf(f, "%% @%d: Foreground %d %d %d\n", i, buf.getByte(i+1), + buf.getByte(i+2), buf.getByte(i+3)); + fprintf(f, "%d %d %d FG\n", buf.getByte(i+1), + buf.getByte(i+2), buf.getByte(i+3)); + i += 4; + } + break; + case 0x0e: { + // ??? + fprintf(f, "%% @%d: U0e %d\n", i, buf.getByte(i+1)); + debuglog("@%d: U0e %d", i, buf.getByte(i+1)); + i += 2; + } + break; + case 0x0f: { + // ??? + fprintf(f, "%% @%d: U0f %d %d\n", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + debuglog("@%d: U0f %d %d", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + i += 9; + } + break; + case 0x10: { + // background color + fprintf(f, "%% @%d: Background %d %d %d\n", i, buf.getByte(i+1), + buf.getByte(i+2), buf.getByte(i+3)); + fprintf(f, "%d %d %d BG\n", buf.getByte(i+1), + buf.getByte(i+2), buf.getByte(i+3)); + i += 4; + } + break; + case 0x11: { + // ??? + fprintf(f, "%% @%d: U11 %d\n", i, buf.getByte(i+1)); + debuglog("@%d: U11 %d", i, buf.getByte(i+1)); + i += 2; + } + break; + case 0x17: { + // ??? + fprintf(f, "%% @%d: U17 %d %d\n", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + debuglog("@%d: U17 %d %d", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + i += 9; + } + break; + case 0x19: { + // Draw line + fprintf(f, "%% @%d: Line %d %d %d %d\n", i, + buf.getDWord(i+1), buf.getDWord(i+5), + buf.getDWord(i+9), buf.getDWord(i+13)); + fprintf(f, "%d %d %d %d L\n", + buf.getDWord(i+1), buf.getDWord(i+5), + buf.getDWord(i+9), buf.getDWord(i+13)); + i += 17; + } + break; + case 0x1b: { + // ??? + fprintf(f, "%% @%d: U1b %d %d\n", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + debuglog("@%d: U1b %d %d", i, buf.getDWord(i+1), + buf.getDWord(i+5)); + i += 9; + } + break; + case 0x1f: { + // Draw ellipse + fprintf(f, "%% @%d: Ellipse %d %d %d %d\n", i, + buf.getDWord(i+1), buf.getDWord(i+5), + buf.getDWord(i+9), buf.getDWord(i+13)); + fprintf(f, "%d %d %d %d E\n", + buf.getDWord(i+1), buf.getDWord(i+5), + buf.getDWord(i+9), buf.getDWord(i+13)); + i += 17; + } + break; + case 0x20: { + // Draw rectangle + fprintf(f, "%% @%d: Rectangle %d %d %d %d\n", i, + buf.getDWord(i+1), buf.getDWord(i+5), + buf.getDWord(i+9), buf.getDWord(i+13)); + fprintf(f, "%d %d %d %d R\n", + buf.getDWord(i+1), buf.getDWord(i+5), + buf.getDWord(i+9), buf.getDWord(i+13)); + i += 17; + } + break; + + case 0x23: { + // Draw polygon + unsigned long count = buf.getDWord(i+1); + int o = i + 5; + fprintf(f, "%% @%d: Polygon (%d segments)\n", i, count); + fprintf(f, "[\n"); + for (int j = 0; j < count; j++) { + fprintf(f, "%d %d\n", buf.getDWord(o), + buf.getDWord(o+4)); + o += 8; + } + fprintf(f, "] P\n"); + i = o + 1; + } + break; + case 0x25: { + // Draw bitmap + // skip for now + unsigned long blen = buf.getDWord(i+17); + fprintf(f, "%% @%d: U25\n", i); + debuglog("@%d: U25 len=%d ofs=%d", i, blen, buf.getDWord(i+21)); + i += (17 + blen); + } + break; + case 0x26: { + // Draw bitmap + unsigned long llx = buf.getDWord(i+1); + unsigned long lly = buf.getDWord(i+13); + unsigned long urx = buf.getDWord(i+9); + unsigned long ury = buf.getDWord(i+5); + unsigned long bw = buf.getDWord(i+25); + unsigned long bh = buf.getDWord(i+29); + unsigned long blen = buf.getDWord(i+17); + unsigned long bofs = buf.getDWord(i+21); + unsigned long bits = buf.getDWord(i+41); + bool rle = (buf.getDWord(i+53) == 1); + fprintf(f, "%% @%d: Bitmap\n", i); + ps_bitmap(f, llx, lly, urx, ury, buf.getString(17)); + debuglog("Bitmap len=%d ofs=%d", blen, bofs); + i += (17 + blen + 16); + } + break; + case 0x27: { + // Draw label + int tlen; + int ofs; + if (buf.getByte(i+1) & 1) { + tlen = buf.getWord(i+1) >> 3; + ofs = i + 3; + } else { + tlen = buf.getByte(i+1) >> 2; + ofs = i + 2; + } + string text(buf.getString(ofs), tlen); + ofs += tlen; + ps_escape(text); + fprintf(f, "(%s) %d %d 0 0 false T\n", text.c_str(), + buf.getDWord(ofs), buf.getDWord(ofs+4) + boffset); + i = ofs + 8; + } + break; + case 0x28: { + // Draw text + int tlen; + int ofs; + if (buf.getByte(i+1) & 1) { + tlen = buf.getWord(i+1) >> 3; + ofs = i + 3; + } else { + tlen = buf.getByte(i+1) >> 2; + ofs = i + 2; + } + string text(buf.getString(ofs), tlen); + ofs += tlen; + int x = buf.getDWord(ofs); + fprintf(f, "%% @%d: Text '%s' %d %d %d %d ?%d ?%d\n", i, + text.c_str(), x, buf.getDWord(ofs+12) + boffset, + buf.getDWord(ofs+4), buf.getDWord(ofs+8), + buf.getByte(ofs+16), buf.getDWord(ofs+17)); + ps_escape(text); + if ((x == 0) && (lmargin != -1)) + x = lmargin; + fprintf(f, "(%s) %d %d %d %d true T\n", text.c_str(), + x, buf.getDWord(ofs+12) + boffset, + buf.getDWord(ofs+4), buf.getDWord(ofs+8)); + i = ofs + 25; + } + break; + default: + debuglog("@%d: UNHANDLED OPCODE %02x", i, opcode); + i++; + break; + } + } + fprintf(f, "showpage\n"); + if (last) { + fputs( + "%%Trailer\n" + "%%DocumentNeededResources: ", f); + if (usedfonts.empty()) + fputs("none\n", f); + else { + usedfonts.erase(0, 4); + fputs(usedfonts.c_str(), f); + } + fprintf(f, "%%%%Pages: %d\n", page + 1); + fprintf(f, "%%%%BoundingBox: %d %d %d %d\n", + minx / 20, miny / 20, maxx / 20, maxy / 20); + fputs("%%EOF\n", f); + } } +static unsigned char fakePage[15] = { + 0x2a, 0x2a, 0x09, 0x00, 0x00, 0x00, 0x82, 0x2e, + 0x00, 0x00, 0xc6, 0x41, 0x00, 0x00, 0x00, +}; + +const static void service_loop() { serviceLoop = true; while (serviceLoop) { bool spoolOpen = false; - bufferStore c; + bool pageStart = true; + bool cancelled = false; + unsigned long plen; + int pageCount; + bufferStore buf; + bufferStore pageBuf; int fd; + FILE *f; + unsigned char b; char *jname = (char *)malloc(strlen(spooldir) + strlen(TEMPLATE) + 2); while (1) { /* Job loop */ - c.init(); - if (wPrt->getData(c) != rfsv::E_PSI_GEN_NONE) { - if (spoolOpen) { - unlink(jname); - close(fd); - errorlog("Job aborted"); + buf.init(); + if (wPrt->getData(buf) == rfsv::E_PSI_GEN_NONE) { + if ((buf.getLen() == 15) && + (!memcmp(buf.getString(0), fakePage, 15))) { + cancelled = false; + if (spoolOpen) { + fclose(f); + infolog("Cancelled job %s", jname); + unlink(jname); + break; + } + continue; + } + if (!spoolOpen && !cancelled) { + sprintf(jname, "%s/%s", spooldir, TEMPLATE); + if ((fd = mkstemp(jname)) != -1) { + infolog("Receiving new job %s", jname); + spoolOpen = true; + pageStart = true; + pageCount = 0; + } else { + errorlog("Could not create spool file."); + cancelled = true; + wPrt->cancelJob(); + } + f = fdopen(fd, "w"); + plen = 0; + } + b = buf.getByte(0); + if ((b != 0x2a) && (b != 0xff)) { + errorlog("Invalid packet type 0x%02x.", b); + cancelled = true; + wPrt->cancelJob(); + } + bool jobEnd = (b == 0xff); + if (!cancelled) { + buf.discardFirstBytes(1); + if (pageStart) { + b = buf.getByte(0); + plen = buf.getDWord(1) - 8; + buf.discardFirstBytes(5+8); + pageStart = false; + pageBuf.init(); + } + pageBuf.addBuff(buf); + plen -= buf.getLen(); + if (plen <= 0) { + convertPage(f, pageCount++, jobEnd, pageBuf); + pageBuf.init(); + pageStart = true; + } + } + if (jobEnd) { + if (spoolOpen) + fclose(f); + if (!cancelled) { + if (pageCount > 0) { + infolog("Spooling %d pages", pageCount); + // TODO: print it... + } + } else + unlink(jname); + spoolOpen = false; } - free(jname); - return; - } - if ((c.getLen() == 15) && (c.getWord(0) == 0x2a2a)) { - sprintf(jname, "%s/%s", spooldir, TEMPLATE); - if ((fd = mkstemp(jname)) != -1) { - debuglog("Receiving new job %s", jname); - write(fd, c.getString(0), c.getLen()); - spoolOpen = true; - } else - errorlog("Could not create spool file."); - } else { - if (spoolOpen) - write(fd, c.getString(0), c.getLen()); - if (c.getWord(0) == 0xffff) - break; } } - if (spoolOpen) { - close(fd); - spoolOpen = false; - debuglog("Job received, start conversion ..."); - convert_job(jname); - } free(jname); } } diff --git a/plpprint/prolog.ps.in b/plpprint/prolog.ps.in new file mode 100644 index 0000000..4321eaf --- /dev/null +++ b/plpprint/prolog.ps.in @@ -0,0 +1,159 @@ +%%BeginResource: plpprint_prolog @VERSION@ 0 +% $Id$ +/ul false def +/st false def +/bg [0 0 0] def +/fg [0 0 0] def +/twips{1440 div 72 mul}bind def +/EpocEncoding ISOLatin1Encoding 256 array copy dup 128 +[/Euro/.notdef/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl +/circumflex/perthousand/Scaron/guilsinglleft/OE/.notdef/Zcaron/.notdef +/.notdef/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash +/emdash/tilde/trademark/scaron/guilsinglright/oe/.notdef/zcaron/ydieresis] +putinterval def +/fs{ % fontvalue fs pointvalue + 1000 div 24 mul +}bind def +/m{ + top exch sub moveto +}bind def +/l{ + top exch sub lineto +}bind def +/UL{ + 1 eq /ul exch def +}bind def +/ST{ + 1 eq /st exch def +}bind def +/BG{ % r g b BG - (store background color) + mark 4 1 roll ] /bg exch def +}bind def +/FG{ % r g b FG - (store foreground color) + mark 4 1 roll ] /fg exch def +}bind def +/SC{ % colorarray SC - (set stored color) + dup 0 get 255 div exch + dup 1 get 255 div exch + 2 get 255 div + setrgbcolor +}bind def +/F{ + findfont + % reencode for ISOLatin1. (From redbook sec. 5.6.1) and add some + % special symbols, resulting in an EPOC encoding + dup length dict begin + { + 1 index dup /FID ne { + /CharStrings ne { + def + } { + % must copy CharStrings dict to make it writeable + dup length dict begin {def} forall + % copy /Euro charstring from Symbol font + /Euro /Symbol findfont /CharStrings get /Euro get def + currentdict end def + } ifelse + } { + pop pop pop + } ifelse + } forall + % Replace encoding + /Encoding EpocEncoding def + currentdict + end dup /FontName get 80 string cvs (-EPOCLatin15) concatstrings cvn + exch definefont + % end of reencoding + exch twips scalefont setfont +}bind def +/L{ % x1 y1 x2 y2 L - (draw line from x1,y1 to x2,y2) + 4 -1 roll twips 4 -1 roll twips m + twips exch twips exch l fg SC stroke +}bind def +/R{ % left top right bottom R - (draw rectangle) + 4 dict begin + twips /y2 exch def + twips /x2 exch def + twips /y1 exch def + twips /x1 exch def + newpath + x1 y1 m x2 y1 l x2 y2 l x1 y2 l closepath + gsave bg SC fill grestore fg SC stroke + end +}bind def +/E { % ulx uly llx lly E - (draw ellipse) + 6 dict begin + twips /lly exch def + twips /llx exch def + twips /uly exch def + twips /ulx exch def + /wx llx ulx sub def + /wy lly uly sub def + gsave + newpath + ulx wx 2 div add top uly sub wy 2 div sub translate + 1 wy wx div scale + newpath wx 2 div 0 moveto + 0 0 wx 2 div 0 360 arc closepath + gsave bg SC fill grestore fg SC stroke + grestore + end +}bind def +/P{ % pointarray P - (draw polygon) + 4 dict begin + /points exch def + 0 2 points length 1 sub { + /idx exch def + points idx get twips + points idx 1 add get twips + idx 0 eq {m}{l} ifelse + } for + gsave bg SC fill grestore fg SC stroke + end +}bind def +/T{ % string left bottom top right justify T - (draw text) + 5 dict begin + /just exch def + twips /x2 exch def + twips /y2 exch def + twips /y1 exch def + twips /x1 exch def + x1 y1 m + gsave + just pop false { + gsave + newpath 0 0 moveto dup false charpath pathbbox + grestore + 4 1 roll exch sub 3 1 roll sub % width height + y2 y1 sub exch div exch % yscale width + x2 x1 sub exch div exch % xscale yscale + scale + } if + ul { + gsave + currentfont /FontInfo known { + currentfont /FontInfo get begin + 0 UnderlinePosition fs rmoveto + UnderlineThickness fs setlinewidth + end + } { + 0 -10 rmoveto 0.5 setlinewidth + } ifelse + dup stringwidth rlineto stroke + grestore + } if + st { + gsave + newpath 0 0 moveto (I) false charpath pathbbox + exch pop exch sub exch pop 2 div 0 exch + grestore + gsave + rmoveto + dup stringwidth rlineto stroke + grestore + } if + show + grestore + end +}bind def +%%EndResource -- cgit v1.2.3