TextFormat.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:39k
- /* $Id: TextFormat.c++,v 1.7 2009/07/29 01:40:06 faxguy Exp $ */
- /*
- * Copyright (c) 1993-1996 Sam Leffler
- * Copyright (c) 1993-1996 Silicon Graphics, Inc.
- * HylaFAX is a trademark of Silicon Graphics
- *
- * Permission to use, copy, modify, distribute, and sell this software and
- * its documentation for any purpose is hereby granted without fee, provided
- * that (i) the above copyright notices and this permission notice appear in
- * all copies of the software and related documentation, and (ii) the names of
- * Sam Leffler and Silicon Graphics may not be used in any advertising or
- * publicity relating to the software without the specific, prior written
- * permission of Sam Leffler and Silicon Graphics.
- *
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
- *
- * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
- * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
- * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
- */
- /*
- * Text to PostScript conversion and formatting support.
- *
- * This class takes ASCII text and produces PostScript
- * doing formatting using the font metric information
- * for a single font. Multi-column output, landscape and
- * portrait page orientation, and various other controls
- * are supported. Page headers a la the old enscript
- * program from Adobe can also be added. This code is
- * distantly related to the lptops program by Nelson Beebe.
- */
- #include "config.h"
- #include "Array.h"
- #include "Dictionary.h"
- #include "PageSize.h"
- #include "TextFormat.h"
- #include "Sys.h"
- #include <ctype.h>
- #include <errno.h>
- #if HAS_MMAP
- #include <sys/mman.h>
- #endif
- #define LUNIT (72*20) // local coord system is .05 scale
- #define ICVT(x) ((TextCoord)((x)*LUNIT)) // scale inches to local coordinates
- #define CVTI(x) (float(x)/LUNIT) // convert coords to inches
- inline TextCoord fxmax(TextCoord a, TextCoord b)
- { return (a > b) ? a : b; }
- #define COLFRAC 35 // 1/fraction of col width for margin
- fxDECLARE_PrimArray(OfftArray, off_t)
- fxIMPLEMENT_PrimArray(OfftArray, off_t)
- fxDECLARE_StrKeyDictionary(FontDict, TextFont*)
- fxIMPLEMENT_StrKeyPtrValueDictionary(FontDict, TextFont*)
- TextFormat::TextFormat()
- {
- output = NULL;
- tf = NULL;
- pageOff = new OfftArray;
- firstPageNum = 1; // starting page number
- column = 1; // current text column # (1..numcol)
- pageNum = 1; // current page number
- workStarted = false;
- fonts = new FontDict;
- curFont = addFont("Roman", "Courier");
- TextFormat::setupConfig(); // NB: virtual
- }
- TextFormat::~TextFormat()
- {
- for (FontDictIter iter(*fonts); iter.notDone(); iter++)
- delete iter.value();
- delete fonts;
- if (tf != NULL)
- fclose(tf);
- tf = NULL;
- }
- void
- TextFormat::warning(const char* fmt ...) const
- {
- fputs("Warning, ", stderr);
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fputs(".n", stderr);
- }
- void
- TextFormat::error(const char* fmt ...) const
- {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fputs(".n", stderr);
- }
- void
- TextFormat::fatal(const char* fmt ...) const
- {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fputs(".n", stderr);
- exit(1);
- }
- TextFont*
- TextFormat::addFont(const char* name, const char* family)
- {
- TextFont* f = new TextFont(family);
- (*fonts)[name] = f;
- if (workStarted) {
- fxStr emsg;
- if (!f->readMetrics(pointSize, useISO8859, emsg))
- error("Font %s: %s", f->getFamily(), (const char*) emsg);
- }
- return (f);
- }
- const TextFont*
- TextFormat::getFont(const char* name) const
- {
- return (*fonts)[name];
- }
- void TextFormat::setFont(TextFont* f) { curFont = f; }
- void TextFormat::setFont(const char* name) { curFont = (*fonts)[name]; }
- void TextFormat::setFontPath(const char* path) { TextFont::fontPath = path; }
- void TextFormat::setOutputFile(FILE* f) { output = f; }
- void TextFormat::setNumberOfColumns(u_int n) { numcol = n; }
- void TextFormat::setPageHeaders(bool b) { headers = b; }
- void TextFormat::setISO8859(bool b) { useISO8859 = b; }
- void TextFormat::setLineWrapping(bool b) { wrapLines = b; }
- void TextFormat::setOutlineMargin(TextCoord o) { outline = o; }
- void TextFormat::setTextPointSize(TextCoord p) { pointSize = p; }
- void TextFormat::setPageOrientation(u_int o) { landscape = (o == LANDSCAPE); }
- void TextFormat::setPageCollation(u_int c) { reverse = (c == REVERSE); }
- void TextFormat::setTextLineHeight(TextCoord h) { lineHeight = h; }
- void TextFormat::setTitle(const char* cp) { title = cp; }
- void TextFormat::setFilename(const char* cp) { curFile = cp; }
- void
- TextFormat::setGaudyHeaders(bool b)
- {
- gaudy = b;
- if (gaudy)
- headers = true;
- }
- bool
- TextFormat::setTextFont(const char* name)
- {
- if (TextFont::findFont(name)) {
- (*fonts)["Roman"]->family = name;
- return (true);
- } else
- return (false);
- }
- /*
- * Parse margin syntax: l=#,r=#,t=#,b=#
- */
- bool
- TextFormat::setPageMargins(const char* s)
- {
- for (const char* cp = s; cp && cp[0];) {
- if (cp[1] != '=')
- return (false);
- TextCoord v = inch(&cp[2]);
- switch (tolower(cp[0])) {
- case 'b': bm = v; break;
- case 'l': lm = v; break;
- case 'r': rm = v; break;
- case 't': tm = v; break;
- default:
- return (false);
- }
- cp = strchr(cp, ',');
- if (cp)
- cp++;
- }
- return (true);
- }
- void
- TextFormat::setPageMargins(TextCoord l, TextCoord r, TextCoord b, TextCoord t)
- {
- lm = l;
- rm = r;
- bm = b;
- tm = t;
- }
- bool
- TextFormat::setPageSize(const char* name)
- {
- PageSizeInfo* info = PageSizeInfo::getPageSizeByName(name);
- if (info) {
- setPageWidth(info->width() / 25.4);
- setPageHeight(info->height() / 25.4);
- delete info;
- return (true);
- } else
- return (false);
- }
- void TextFormat::setPageWidth(float pw) { physPageWidth = pw; }
- void TextFormat::setPageHeight(float ph) { physPageHeight = ph; }
- void
- TextFormat::setModTimeAndDate(time_t t)
- {
- struct tm* tm = localtime(&t);
- char buf[30];
- strftime(buf, sizeof (buf), "%X", tm); modTime = buf;
- strftime(buf, sizeof (buf), "%F", tm); modDate = buf;
- }
- void TextFormat::setModTime(const char* cp) { modTime = cp; }
- void TextFormat::setModDate(const char* cp) { modDate = cp; }
- void
- TextFormat::beginFormatting(FILE* o)
- {
- output = o;
- pageHeight = ICVT(physPageHeight);
- pageWidth = ICVT(physPageWidth);
- /*
- * Open the file w+ so that we can reread the temp file.
- */
- tf = Sys::tmpfile();
- if (tf == NULL)
- fatal("Cannot open temporary file: %s", strerror(errno));
- numcol = fxmax(1,numcol);
- if (pointSize == -1)
- pointSize = inch(numcol > 1 ? "7bp" : "10bp");
- else
- pointSize = fxmax(inch("3bp"), pointSize);
- if (pointSize > inch("18bp"))
- warning("point size is unusually large (>18pt)");
- // read font metrics
- for (FontDictIter iter(*fonts); iter.notDone(); iter++) {
- fxStr emsg;
- TextFont* f = iter.value();
- if (!f->readMetrics(pointSize, useISO8859, emsg))
- error("Font %s: %s", f->getFamily(), (const char*) emsg);
- }
- outline = fxmax(0L,outline);
- curFont = (*fonts)["Roman"];
- tabWidth = tabStop * curFont->charwidth(' ');
- if (landscape) {
- TextCoord t = pageWidth;
- pageWidth = pageHeight;
- pageHeight = t;
- }
- if (lm+rm >= pageWidth)
- fatal("Margin values too large for page; lm %lu rm %lu page width %lu",
- lm, rm, pageWidth);
- if (tm+bm >= pageHeight)
- fatal("Margin values too large for page; tm %lu bm %lu page height %lu",
- tm, bm, pageHeight);
- col_width = (pageWidth - (lm + rm))/numcol;
- if (numcol > 1 || outline)
- col_margin = col_width/COLFRAC;
- else
- col_margin = 0;
- /*
- * TeX's baseline skip is 12pt for
- * 10pt type, we preserve that ratio.
- */
- if (lineHeight <= 0)
- lineHeight = (pointSize * 12L) / 10L;
- workStarted = true;
- }
- void
- TextFormat::endFormatting(bool skiptrailer)
- {
- emitPrologue();
- /*
- * Now rewind temporary file and write
- * pages to stdout in appropriate order.
- */
- if (reverse) {
- rewind(tf);
- off_t last = (*pageOff)[pageOff->length()-1];
- for (int k = pageNum-firstPageNum; k >= 0; --k) {
- /* copy remainder in reverse order */
- off_t next = (off_t) ftell(stdout);
- Copy_Block((*pageOff)[k],last-1);
- last = (*pageOff)[k];
- (*pageOff)[k] = next;
- }
- } else {
- off_t last = ftell(tf);
- rewind(tf);
- Copy_Block(0L, last-1);
- }
- if (fclose(tf))
- fatal("Close failure on temporary file: %s", strerror(errno));
- tf = NULL;
- if (!skiptrailer) emitTrailer();
- fflush(output);
- workStarted = false;
- }
- /* copy bytes b1..b2 to stdout */
- void
- TextFormat::Copy_Block(off_t b1, off_t b2)
- {
- char buf[16*1024];
- for (off_t k = b1; k <= b2; k += sizeof (buf)) {
- size_t cc = (size_t) fxmin(sizeof (buf), (size_t) (b2-k+1));
- fseek(tf, (long) k, SEEK_SET); // position to desired block
- if (fread(buf, 1, (size_t) cc, tf) != cc)
- fatal("Read error during reverse collation: %s", strerror(errno));
- if (fwrite(buf, 1, (size_t) cc, output) != cc)
- fatal("Output write error: %s", strerror(errno));
- }
- }
- static const char* ISOprologue2 = "
- /reencodeISO{n
- dup length dict beginn
- {1 index /FID ne {def}{pop pop} ifelse} foralln
- /Encoding ISOLatin1Encoding defn
- currentdictn
- endn
- }defn
- /findISO{n
- dup /FontType known{n
- dup /FontType get 3 nen
- 1 index /CharStrings known{n
- 1 index /CharStrings get /Thorn knownn
- }{false}ifelsen
- andn
- }{false}ifelsen
- }defn
- ";
- /*
- * Yech, instead of a single string that we fputs, we
- * break things up into smaller chunks to satisfy braindead
- * compilers...
- */
- void
- TextFormat::putISOPrologue(void)
- {
- fputs("/ISOLatin1Encoding where{pop save true}{false}ifelsen", output);
- fputs("/ISOLatin1Encoding[n", output);
- fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
- fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
- fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
- fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
- fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
- fputs(" /.notdef /.notdef /space /exclam /quotedbl /numbersignn", output);
- fputs(" /dollar /percent /ampersand /quoteright /parenleftn", output);
- fputs(" /parenright /asterisk /plus /comma /minus /periodn", output);
- fputs(" /slash /zero /one /two /three /four /five /six /sevenn", output);
- fputs(" /eight /nine /colon /semicolon /less /equal /greatern", output);
- fputs(" /question /at /A /B /C /D /E /F /G /H /I /J /K /L /Mn", output);
- fputs(" /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleftn", output);
- fputs(" /backslash /bracketright /asciicircum /underscoren", output);
- fputs(" /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /mn", output);
- fputs(" /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleftn", output);
- fputs(" /bar /braceright /asciitilde /guilsinglright /fractionn", output);
- fputs(" /florin /quotesingle /quotedblleft /guilsinglleft /fin", output);
- fputs(" /fl /endash /dagger /daggerdbl /bullet /quotesinglbasen", output);
- fputs(" /quotedblbase /quotedblright /ellipsis /trademarkn", output);
- fputs(" /perthousand /grave /scaron /circumflex /Scaron /tilden", output);
- fputs(" /breve /zcaron /dotaccent /dotlessi /Zcaron /ringn", output);
- fputs(" /hungarumlaut /ogonek /caron /emdash /space /exclamdownn", output);
- fputs(" /cent /sterling /currency /yen /brokenbar /sectionn", output);
- fputs(" /dieresis /copyright /ordfeminine /guillemotleftn", output);
- fputs(" /logicalnot /hyphen /registered /macron /degreen", output);
- fputs(" /plusminus /twosuperior /threesuperior /acute /mun", output);
- fputs(" /paragraph /periodcentered /cedilla /onesuperiorn", output);
- fputs(" /ordmasculine /guillemotright /onequarter /onehalfn", output);
- fputs(" /threequarters /questiondown /Agrave /Aacuten", output);
- fputs(" /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedillan", output);
- fputs(" /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacuten", output);
- fputs(" /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacuten", output);
- fputs(" /Ocircumflex /Otilde /Odieresis /multiply /Oslashn", output);
- fputs(" /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thornn", output);
- fputs(" /germandbls /agrave /aacute /acircumflex /atilden", output);
- fputs(" /adieresis /aring /ae /ccedilla /egrave /eacuten", output);
- fputs(" /ecircumflex /edieresis /igrave /iacute /icircumflexn", output);
- fputs(" /idieresis /eth /ntilde /ograve /oacute /ocircumflexn", output);
- fputs(" /otilde /odieresis /divide /oslash /ugrave /uacuten", output);
- fputs(" /ucircumflex /udieresis /yacute /thorn /ydieresisn", output);
- fputs("]def{restore}ifn", output);
- fputs(ISOprologue2, output);
- }
- static const char* defPrologue = "
- /Cols %u defn
- /PageWidth %.2f defn
- /PageHeight %.2f defn
- /LH %u defn
- /B{gsave}defn
- /LN{show}defn
- /EL{grestore 0 -%d rmoveto}defn
- /M{0 rmoveto}defn
- /O{gsave show grestore}defn
- /LandScape{90 rotate 0 -%ld translate}defn
- /U{%d mul}defn
- /UP{U 72 div}defn
- /S{show grestore 0 -%d rmoveto}defn
- ";
- static const char* headerPrologue1 = "
- /InitGaudyHeaders{n
- /HeaderY exch def /BarLength exch defn
- /ftD /Times-Bold findfont 12 UP scalefont defn
- /ftF /Times-Roman findfont 14 UP scalefont defn
- /ftP /Helvetica-Bold findfont 30 UP scalefont defn
- /fillbox{ % w h x y => -n
- moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineton
- closepath filln
- }defn
- /LB{ % x y w h (label) font labelColor boxColor labelPtSize => -n
- gsaven
- /pts exch UP def /charcolor exch def /boxcolor exch defn
- /font exch def /label exch defn
- /h exch def /w exch defn
- /y exch def /x exch defn
- boxcolor setgray w h x y fillboxn
- /lines label length defn
- /ly y h add h lines pts mul sub 2 div sub pts .85 mul sub defn
- font setfont charcolor setgrayn
- label {n
- dup stringwidth popn
- 2 div x w 2 div add exch sub ly moveton
- shown
- /ly ly pts sub defn
- } foralln
- grestoren
- }defn
- /Header{ % (file) [(date)] (page) => -n
- /Page exch def /Date exch def /File exch defn
- .25 U HeaderY U BarLength .1 sub U .25 U [File] ftF .97 0 14 LBn
- .25 U HeaderY .25 add U BarLength .1 sub U .25 U [()] ftF 1 0 14 LBn
- .25 U HeaderY U 1 U .5 U Date ftD .7 0 12 LBn
- BarLength .75 sub U HeaderY U 1 U .5 U [Page] ftP .7 1 30 LBn
- 1 1 Cols 1 sub{n
- BarLength Cols div mul .19 add U HeaderY U moveto 0 -10 U rlineto stroken
- }forn
- }defn
- }defn
- ";
- static const char* headerPrologue2 = "
- /InitNormalHeaders{n
- /HeaderY exch def /BarLength exch defn
- /ftF /Times-Roman findfont 14 UP scalefont defn
- /ftP /Helvetica-Bold findfont 14 UP scalefont defn
- /LB{ % x y w h (label) font labelColor labelPtSize => -n
- gsaven
- /pts exch UP def /charcolor exch defn
- /font exch def /label exch defn
- /h exch def /w exch defn
- /y exch def /x exch defn
- /ly y h add h pts sub 2 div sub pts .85 mul sub defn
- font setfont charcolor setgrayn
- label stringwidth pop 2 div x w 2 div add exch sub ly moveton
- label shown
- grestoren
- }defn
- /Header{ % (file) [(date)] (page) => -n
- /Page exch def pop /File exch defn
- .25 U HeaderY U BarLength 2 div U .5 U File ftF 0 14 LBn
- BarLength .75 sub U HeaderY U 1 U .5 U Page ftP 0 14 LBn
- 1 1 Cols 1 sub{n
- BarLength Cols div mul .19 add U HeaderY U moveto 0 -10 U rlineto stroken
- }forn
- }defn
- }defn
- /InitNullHeaders{/Header{3{pop}repeat}def pop pop}defn
- ";
- /*
- * Emit the DSC header comments and prologue.
- */
- void
- TextFormat::emitPrologue(void)
- {
- fputs("%!PS-Adobe-3.0n", output);
- fprintf(output, "%%%%Creator: HylaFAX TextFormat Classn");
- fprintf(output, "%%%%Title: %sn", (const char*) title);
- time_t t = Sys::now();
- fprintf(output, "%%%%CreationDate: %s", ctime(&t));
- char* cp = getlogin();
- fprintf(output, "%%%%For: %sn", cp ? cp : "");
- fputs("%%Origin: 0 0n", output);
- fprintf(output, "%%%%BoundingBox: 0 0 %.0f %.0fn",
- physPageWidth*72, physPageHeight*72);
- fputs("%%Pages: (atend)n", output);
- fprintf(output, "%%%%PageOrder: %sn",
- reverse ? "Descend" : "Ascend");
- fprintf(output, "%%%%Orientation: %sn",
- landscape ? "Landscape" : "Portrait");
- fprintf(output, "%%%%DocumentNeededResources: font");
- FontDictIter iter;
- for (iter = *fonts; iter.notDone(); iter++) {
- TextFont* f = iter.value();
- fprintf(output, " %s", f->getFamily());
- }
- fputc('n', output);
- if (gaudy) {
- fputs("%%+ font Times-Boldn", output);
- fputs("%%+ font Times-Romann", output);
- fputs("%%+ font Helvetica-Boldn", output);
- }
- emitClientComments(output);
- fprintf(output, "%%%%EndCommentsn");
- fprintf(output, "%%%%BeginPrologn");
- fputs("/$printdict 50 dict def $printdict beginn", output);
- if (useISO8859)
- putISOPrologue();
- fprintf(output, defPrologue
- , numcol
- , CVTI(pageWidth - (lm+rm))
- , CVTI(pageHeight - (tm+bm))
- , lineHeight
- , lineHeight
- , pageHeight
- , LUNIT
- , lineHeight
- );
- fputs(headerPrologue1, output);
- fputs(headerPrologue2, output);
- fprintf(output, "%.2f %.2f Init%sHeadersn"
- , CVTI(pageWidth - (lm+rm))
- , CVTI(pageHeight - tm)
- , (gaudy ? "Gaudy" : headers ? "Normal" : "Null")
- );
- for (iter = *fonts; iter.notDone(); iter++)
- iter.value()->defFont(output, pointSize, useISO8859);
- emitClientPrologue(output);
- fputs("endn", output);
- fputs("%%EndPrologn", output);
- }
- void TextFormat::emitClientComments(FILE*) {}
- void TextFormat::emitClientPrologue(FILE*) {}
- /*
- * Emit the DSC trailer comments.
- */
- void
- TextFormat::emitTrailer(void)
- {
- fputs("%%Trailern", output);
- fprintf(output, "%%%%Pages: %dn", pageNum - firstPageNum);
- fputs("%%EOFn", output);
- }
- void
- TextFormat::newPage(void)
- {
- x = lm; // x starts at left margin
- right_x = col_width - col_margin/2; // right x, 0-relative
- y = pageHeight - tm - lineHeight; // y at page top
- level = 0; // string paren level reset
- column = 1;
- boc = true;
- bop = true;
- }
- void
- TextFormat::newCol(void)
- {
- x += col_width; // x, shifted
- right_x += col_width; // right x, shifted
- y = pageHeight - tm - lineHeight; // y at page top
- level = 0;
- column++;
- boc = true;
- }
- static void
- putString(FILE* fd, const char* val)
- {
- fputc('(', fd);
- for (; *val; val++) {
- u_int c = *val & 0xff;
- if ((c & 0200) == 0) {
- if (c == '(' || c == ')' || c == '\')
- fputc('\', fd);
- fputc(c, fd);
- } else
- fprintf(fd, "\%03o", c);
- }
- fputc(')', fd);
- }
- void
- TextFormat::beginCol(void)
- {
- if (column == 1) { // new page
- if (reverse) {
- u_int k = pageNum-firstPageNum;
- off_t off = (off_t) ftell(tf);
- if (k < pageOff->length())
- (*pageOff)[k] = off;
- else
- pageOff->append(off);
- }
- fprintf(tf,"%%%%Page: "%d" %dn", pageNum-firstPageNum+1, pageNum);
- fputs("save $printdict beginn", tf);
- fprintf(tf, ".05 dup scalen");
- curFont->setfont(tf);
- if (landscape)
- fputs("LandScapen", tf);
- putString(tf, curFile);
- fputc('[', tf);
- putString(tf, modDate);
- putString(tf, modTime);
- fputc(']', tf);
- fprintf(tf, "(%d)Headern", pageNum);
- }
- fprintf(tf, "%ld %ld moveton",x,y);
- }
- void
- TextFormat::beginLine(void)
- {
- if (boc)
- beginCol(), boc = false, bop = false;
- fputc('B', tf);
- }
- void
- TextFormat::beginText(void)
- {
- fputc('(', tf);
- level++;
- }
- void
- TextFormat::hrMove(TextCoord x)
- {
- fprintf(tf, " %ld M ", x);
- xoff += x;
- }
- void
- TextFormat::closeStrings(const char* cmd)
- {
- int l = level;
- for (; level > 0; level--)
- fputc(')', tf);
- if (l > 0)
- fputs(cmd, tf);
- }
- void
- TextFormat::beginFile(void)
- {
- newPage(); // each file starts on a new page
- bol = true; // force line start
- bot = true; // force text start
- xoff = col_width * (column-1);
- }
- void
- TextFormat::endFile(void)
- {
- if (!bot)
- endTextLine();
- if (!bol)
- endLine();
- if (!bop) {
- column = numcol; // force page end action
- endTextCol();
- }
- if (reverse) {
- /*
- * Normally, beginCol sets the pageOff entry. Since this is
- * the last output for this file, we must set it here manually.
- * If more files are printed, this entry will be overwritten (with
- * the same value) at the next call to beginCol.
- */
- off_t off = (off_t) ftell(tf);
- pageOff->append(off);
- }
- }
- void
- TextFormat::formatFile(const char* name)
- {
- FILE* fp = fopen(name, "r");
- if (fp != NULL) {
- curFile = name;
- formatFile(fp);
- fclose(fp);
- } else
- error("%s: Cannot open file: %s", name, strerror(errno));
- }
- void
- TextFormat::formatFile(FILE* fp)
- {
- #if HAS_MMAP
- struct stat sb;
- Sys::fstat(fileno(fp), sb);
- char* addr = (char*)
- mmap(NULL, (size_t) sb.st_size, PROT_READ, MAP_SHARED, fileno(fp), 0);
- if (addr == (char*) -1) { // revert to file reads
- #endif
- int c;
- while ((c = getc(fp)) == 'f') // discard initial form feeds
- ;
- ungetc(c, fp);
- beginFile();
- format(fp);
- endFile();
- #if HAS_MMAP
- } else {
- const char* cp = addr;
- const char* ep = cp + sb.st_size;
- while (cp < ep && *cp == 'f') // discard initial form feeds
- cp++;
- beginFile();
- format(cp, ep-cp);
- endFile();
- munmap(addr, (size_t) sb.st_size);
- }
- #endif
- }
- void
- TextFormat::format(FILE* fp)
- {
- int c;
- while ((c = getc(fp)) != EOF) {
- switch (c) {
- case ' ': // discard nulls
- break;
- case 'f': // form feed
- if (!bop) {
- endTextCol();
- bol = bot = true;
- }
- break;
- case 'n': // line break
- if (bol)
- beginLine();
- if (bot)
- beginText();
- endTextLine();
- break;
- case 'r': // check for overstriking
- if ((c = getc(fp)) == 'n') {
- ungetc(c,fp); // collapse rn => n
- break;
- }
- closeStrings("On"); // do overstriking
- bot = true; // start new string
- break;
- default:
- TextCoord hm;
- if (c == 't' || c == ' ') {
- /*
- * Coalesce white space into one relative motion.
- * The offset calculation below is to insure that
- * 0 means the start of the line (no matter what
- * column we're in).
- */
- hm = 0;
- int cc = c;
- TextCoord off = xoff - col_width*(column-1);
- do {
- if (cc == 't')
- hm += tabWidth - (off+hm) % tabWidth;
- else
- hm += curFont->charwidth(' ');
- } while ((cc = getc(fp)) == 't' || cc == ' ');
- if (cc != EOF)
- ungetc(cc, fp);
- /*
- * If the motion is one space's worth, either
- * a single blank or a tab that causes a blank's
- * worth of horizontal motion, then force it
- * to be treated as a blank below.
- */
- if (hm == curFont->charwidth(' '))
- c = ' ';
- else
- c = 't';
- } else
- hm = curFont->charwidth(c);
- if (xoff + hm > right_x) {
- if (!wrapLines) // discard line overflow
- break;
- if (c == 't') // adjust white space motion
- hm -= right_x - xoff;
- endTextLine();
- }
- if (bol)
- beginLine(), bol = false;
- if (c == 't') { // close open PS string and do motion
- if (hm > 0) {
- closeStrings("LN");
- bot = true; // force new string
- hrMove(hm);
- }
- } else { // append to open PS string
- if (bot)
- beginText(), bot = false;
- if (040 <= c && c <= 0176) {
- if (c == '(' || c == ')' || c == '\')
- fputc('\',tf);
- fputc(c,tf);
- } else
- fprintf(tf, "\%03o", c & 0xff);
- xoff += hm;
- }
- break;
- }
- }
- }
- void
- TextFormat::format(const char* cp, u_int cc)
- {
- const char* ep = cp+cc;
- while (cp < ep) {
- int c = *cp++ & 0xff;
- switch (c) {
- case ' ': // discard nulls
- break;
- case 'f': // form feed
- if (!bop) {
- endTextCol();
- bol = bot = true;
- }
- break;
- case 'n': // line break
- if (bol)
- beginLine();
- if (bot)
- beginText();
- endTextLine();
- break;
- case 'r': // check for overstriking
- if (cp < ep && *cp == 'n')
- break; // collapse rn => n
- cp++; // count character
- closeStrings("On"); // do overstriking
- bot = true; // start new string
- break;
- default:
- TextCoord hm;
- if (c == 't' || c == ' ') {
- /*
- * Coalesce white space into one relative motion.
- * The offset calculation below is to insure that
- * 0 means the start of the line (no matter what
- * column we're in).
- */
- hm = 0;
- int cc = c;
- TextCoord off = xoff - col_width*(column-1);
- do {
- if (cc == 't')
- hm += tabWidth - (off+hm) % tabWidth;
- else
- hm += curFont->charwidth(' ');
- } while (cp < ep && ((cc = *cp++) == 't' || cc == ' '));
- if (cc != 't' && cc != ' ')
- cp--;
- /*
- * If the motion is one space's worth, either
- * a single blank or a tab that causes a blank's
- * worth of horizontal motion, then force it
- * to be treated as a blank below.
- */
- if (hm == curFont->charwidth(' '))
- c = ' ';
- else
- c = 't';
- } else
- hm = curFont->charwidth(c);
- if (xoff + hm > right_x) {
- if (!wrapLines) // discard line overflow
- break;
- if (c == 't') // adjust white space motion
- hm -= right_x - xoff;
- endTextLine();
- }
- if (bol)
- beginLine(), bol = false;
- if (c == 't') { // close open PS string and do motion
- if (hm > 0) {
- closeStrings("LN");
- fprintf(tf, " %ld M ", hm);
- bot = true; // force new string
- }
- } else { // append to open PS string
- if (bot)
- beginText(), bot = false;
- if (040 <= c && c <= 0176) {
- if (c == '(' || c == ')' || c == '\')
- fputc('\',tf);
- fputc(c,tf);
- } else
- fprintf(tf, "\%03o", c & 0xff);
- }
- xoff += hm;
- break;
- }
- }
- }
- static const char* outlineCol = "n
- gsave
- %ld setlinewidth
- newpath %ld %ld moveto
- %ld %ld rlineto
- %ld %ld rlineto
- %ld %ld rlineto
- closepath stroke
- grestoren
- ";
- void
- TextFormat::endCol(void)
- {
- if (outline > 0) {
- fprintf(tf, outlineCol, outline,
- x - col_margin, bm, col_width, 0, 0,
- pageHeight-bm-tm, -col_width, 0);
- }
- if (column == numcol) { // columns filled, start new page
- if (!bop) {
- pageNum++;
- fputs("showpagenend restoren", tf);
- flush();
- newPage();
- }
- } else
- if (!boc)
- newCol();
- }
- void
- TextFormat::endPage(void)
- {
- column = numcol;
- endCol();
- }
- void
- TextFormat::endLine(void)
- {
- fputs("ELn", tf);
- if ((y -= lineHeight) < bm)
- endCol();
- xoff = col_width * (column-1);
- }
- void
- TextFormat::endTextCol(void)
- {
- closeStrings("LN");
- fputc('n', tf);
- endCol();
- }
- void
- TextFormat::endTextLine(void)
- {
- closeStrings("Sn");
- if ((y -= lineHeight) < bm)
- endCol();
- xoff = col_width * (column-1);
- bol = bot = true;
- }
- void
- TextFormat::reserveVSpace(TextCoord vs)
- {
- if (y - vs < bm)
- endCol();
- }
- void
- TextFormat::flush(void)
- {
- fflush(tf);
- if (ferror(tf) && errno == ENOSPC)
- fatal("Output write error: %s", strerror(errno));
- }
- /*
- * Convert a value of the form:
- * #.##bp big point (1in = 72bp)
- * #.##cc cicero (1cc = 12dd)
- * #.##cm centimeter
- * #.##dd didot point (1157dd = 1238pt)
- * #.##in inch
- * #.##mm millimeter (10mm = 1cm)
- * #.##pc pica (1pc = 12pt)
- * #.##pt point (72.27pt = 1in)
- * #.##sp scaled point (65536sp = 1pt)
- * to inches, returning it as the function value. The case of
- * the dimension name is ignored. No space is permitted between
- * the number and the dimension.
- */
- TextCoord
- TextFormat::inch(const char* s)
- {
- char* cp;
- double v = strtod(s, &cp);
- if (cp == NULL)
- return (ICVT(0)); // XXX
- if (strncasecmp(cp,"in",2) == 0) // inches
- ;
- else if (strncasecmp(cp,"cm",2) == 0) // centimeters
- v /= 2.54;
- else if (strncasecmp(cp,"pt",2) == 0) // points
- v /= 72.27;
- else if (strncasecmp(cp,"cc",2) == 0) // cicero
- v *= 12.0 * (1238.0 / 1157.0) / 72.27;
- else if (strncasecmp(cp,"dd",2) == 0) // didot points
- v *= (1238.0 / 1157.0) / 72.27;
- else if (strncasecmp(cp,"mm",2) == 0) // millimeters
- v /= 25.4;
- else if (strncasecmp(cp,"pc",2) == 0) // pica
- v *= 12.0 / 72.27;
- else if (strncasecmp(cp,"sp",2) == 0) // scaled points
- v /= (65536.0 * 72.27);
- else // big points
- v /= 72.0;
- return ICVT(v);
- }
- /*
- * Configuration file support.
- */
- void
- TextFormat::setupConfig()
- {
- gaudy = false; // emit gaudy headers
- landscape = false; // horizontal landscape mode output
- useISO8859 = true; // use the ISO 8859-1 character encoding
- reverse = false; // page reversal flag
- wrapLines = true; // wrap/truncate lines
- headers = true; // emit page headers
- pointSize = -1; // font point size in big points
- lm = inch("0.25in"); // left margin
- rm = inch("0.25in"); // right margin
- tm = inch("0.85in"); // top margin
- bm = inch("0.5in"); // bottom margin
- lineHeight = 0; // inter-line spacing
- numcol = 1; // number of text columns
- col_margin = 0L; // inter-column margin
- outline = 0L; // page and column outline linewidth
- tabStop = 8; // 8-column tab stop
- setPageSize("default"); // default system page dimensions
- }
- void
- TextFormat::resetConfig()
- {
- setupConfig();
- }
- void TextFormat::configError(const char* ...) {}
- void TextFormat::configTrace(const char* ...) {}
- #undef streq
- #define streq(a,b) (strcasecmp(a,b)==0)
- bool
- TextFormat::setConfigItem(const char* tag, const char* value)
- {
- if (streq(tag, "columns"))
- setNumberOfColumns(getNumber(value));
- else if (streq(tag, "pageheaders"))
- setPageHeaders(getBoolean(value));
- else if (streq(tag, "linewrap"))
- setLineWrapping(getBoolean(value));
- else if (streq(tag, "iso8859"))
- setISO8859(getBoolean(value));
- else if (streq(tag, "textfont"))
- setTextFont(value);
- else if (streq(tag, "gaudyheaders"))
- setGaudyHeaders(getBoolean(value));
- else if (streq(tag, "pagemargins"))
- setPageMargins(value);
- else if (streq(tag, "outlinemargin"))
- setOutlineMargin(inch(value));
- else if (streq(tag, "textpointsize"))
- setTextPointSize(inch(value));
- else if (streq(tag, "orientation"))
- setPageOrientation(streq(value, "landscape") ? LANDSCAPE : PORTRAIT);
- else if (streq(tag, "pagesize"))
- setPageSize(value);
- else if (streq(tag, "pagewidth"))
- setPageWidth(atof(value));
- else if (streq(tag, "pageheight"))
- setPageHeight(atof(value));
- else if (streq(tag, "pagecollation"))
- setPageCollation(streq(value, "forward") ? FORWARD : REVERSE);
- else if (streq(tag, "textlineheight"))
- setTextLineHeight(inch(value));
- else if (streq(tag, "tabstop"))
- tabStop = getNumber(value);
- else if (streq(tag, "fontmap")) // XXX
- TextFont::fontMap = value;
- else if (streq(tag, "fontpath"))
- setFontPath(value);
- else
- return (false);
- return (true);
- }
- #define NCHARS (sizeof (widths) / sizeof (widths[0]))
- fxStr TextFont::fontMap = _PATH_FONTMAP;
- fxStr TextFont::fontPath = _PATH_AFM;
- u_int TextFont::fontID = 0;
- TextFont::TextFont(const char* cp) : family(cp)
- {
- showproc = fxStr::format("s%u", fontID);
- setproc = fxStr::format("sf%u", fontID);
- fontID++;
- }
- TextFont::~TextFont() {}
- bool
- TextFont::decodeFontName(const char* name, fxStr& filename, fxStr& emsg)
- {
- struct stat junk;
- fxStr path = fontMap;
- u_int index = path.next(0, ':');
- fxStr key = name;
- while (index > 0) {
- /* Newer versions of Ghostscript use "Fontmap.GS"
- * whereas older ones omit the ".GS" extension.
- * If the Fontmap.GS file isn't available default
- * to the older convention.
- */
- filename = path.head(index) | "/" | "Fontmap.GS";
- if (stat(filename, &junk) != 0)
- filename = path.head(index) | "/" | "Fontmap";
- fxStr fontMapFile = filename;
- path.remove(0, index);
- if (path.length() > 0) path.remove(0, 1);
- FILE* fd = Sys::fopen(fontMapFile, "r");
- if (fd != NULL && fontMapFile[0] == '/') {
- char buf[1024];
- int aliascount = maxaliases;
- while (fgets(buf, sizeof(buf), fd) != NULL &&
- aliascount > 0) {
- size_t len = strcspn(buf, "%n");
- if (len == strlen(buf)) {
- emsg = fxStr::format(
- "Warning:%s - line too long.", (const char*)fontMapFile);
- break;
- }
- if (len == 0) continue;
- *(buf + len) = ' ';
- char* tmp = buf + strcspn(buf, ") t");
- *tmp++ = ' ';
- tmp += strspn(tmp, " t");
- if (strcmp(key, buf + 1) == 0) {
- //match - now ensure it is the last one in the file
- // for gs compatibility
- *(tmp + strcspn(tmp, ") t;")) = ' ';
- fxStr val = tmp;
- while (fgets(buf, sizeof(buf), fd) != NULL) {
- len = strcspn(buf, "%n");
- *(buf + len) = ' ';
- if (len == strlen(buf)) {
- emsg = fxStr::format(
- "Warning: %s - line too long.", (const char*) fontMapFile);
- break;
- }
- if (len == 0) continue;
- tmp = buf + strcspn(buf, ") t");
- *tmp++ = ' ';
- tmp += strspn(tmp, " t");
- if (strcmp(key, buf + 1) == 0) {
- *(tmp + strcspn(tmp, ") t;")) = ' ';
- val = tmp;
- }
- }
- if (val[0] == '/') {
- //alias
- aliascount--;
- val.remove(0);
- key = val;
- fseek(fd, 0L, SEEK_SET);
- } else {
- //real file
- key = name;
- fclose(fd);
- val.remove(0);
- int pos = val.next(0, '.');
- val.remove(pos, val.length() - pos);
- val.append(".afm");
- //move through dirs looking for font
- fxStr fpath = fontPath;
- int index2 = fpath.next(0, ':');
- filename = fpath.head(index2) | "/" | val;
- fpath.remove(0, index2);
- if (fpath.length() > 0) fpath.remove(0, 1);
- while (stat(filename, &junk) != 0 && index2 > 0) {
- index2 = fpath.next(0, ':');
- filename = fpath.head(index2) | "/" | val;
- fpath.remove(0, index2);
- if (fpath.length() > 0) fpath.remove(0, 1);
- }
- bool result = stat(filename, &junk) ? false : true;
- if (!result)
- emsg = fxStr::format(
- "Warning: %s invalid Fontmap entry - no filename present", (const char*)val);
- return result;
- }
- }
- }
- fclose(fd);
- }
- index = path.next(0, ':');
- }
- //decoding using fontmap has failed
- //now try plain filename with/without afm extension
- path = fontPath;
- index = path.next(0, ':');
- while (index > 0) {
- filename = path.head(index) | "/" | key | ".afm";
- path.remove(0, index);
- if (path.length() > 0) path.remove(0, 1);
- if (stat(filename, &junk) == 0) return true;
- filename.resize(filename.length()-4); // strip ``.afm''
- if (stat(filename, &junk) == 0) return true;
- index = path.next(0, ':');
- }
- return false;
- }
- bool
- TextFont::findFont(const char* name)
- {
- fxStr myname, emsg;
- return decodeFontName(name, myname, emsg);
- }
- static const char* defISOFont = "
- /%s{/%s findfont
- findISO{reencodeISO /%s-ISO exch definefont}if
- %d UP scalefont setfont
- }defn
- ";
- static const char* defRegularFont = "
- /%s{/%s findfont %d UP scalefont setfont}defn
- ";
- void
- TextFont::defFont(FILE* fd, TextCoord ps, bool useISO8859) const
- {
- if (useISO8859) {
- fprintf(fd, defISOFont, (const char*) setproc,
- (const char*) family, (const char*) family, ps/20, ps/20);
- } else {
- fprintf(fd, defRegularFont, (const char*) setproc,
- (const char*) family, ps/20);
- }
- fprintf(fd, "/%s{%s show}defn",
- (const char*) showproc, (const char*) setproc);
- }
- void
- TextFont::setfont(FILE* fd) const
- {
- fprintf(fd, " %s ", (const char*) setproc);
- }
- TextCoord
- TextFont::show(FILE* fd, const char* val, int len) const
- {
- TextCoord hm = 0;
- if (len > 0) {
- fprintf(fd, "(");
- do {
- u_int c = *val++ & 0xff;
- if ((c & 0200) == 0) {
- if (c == '(' || c == ')' || c == '\')
- fputc('\', fd);
- fputc(c, fd);
- } else
- fprintf(fd, "\%03o", c);
- hm += widths[c]; // Leif Erlingsson <leif@lege.com>
- } while (--len);
- fprintf(fd, ")%s ", (const char*) showproc);
- }
- return (hm);
- }
- TextCoord
- TextFont::show(FILE* fd, const fxStr& s) const
- {
- return show(fd, s, s.length());
- }
- TextCoord
- TextFont::strwidth(const char* cp) const
- {
- TextCoord w = 0;
- while (*cp)
- w += widths[(unsigned) (*cp++ & 0xff)]; // Leif Erlingsson <leif@lege.com>
- return w;
- }
- void
- TextFont::loadFixedMetrics(TextCoord w)
- {
- for (u_int i = 0; i < NCHARS; i++)
- widths[i] = w;
- }
- bool
- TextFont::getAFMLine(FILE* fp, char* buf, int bsize)
- {
- if (fgets(buf, bsize, fp) == NULL)
- return (false);
- char* cp = strchr(buf, 'n');
- if (cp == NULL) { // line too long, skip it
- int c;
- while ((c = getc(fp)) != 'n') // skip to end of line
- if (c == EOF)
- return (false);
- cp = buf; // force line to be skipped
- }
- *cp = ' ';
- return (true);
- }
- FILE*
- TextFont::openAFMFile(fxStr& fontpath)
- {
- fxStr emsg;
- if (!decodeFontName(family, fontpath, emsg)) {
- fprintf(stderr,emsg);
- return NULL;
- }
- return Sys::fopen(fontpath, "r");
- }
- bool
- TextFont::readMetrics(TextCoord ps, bool useISO8859, fxStr& emsg)
- {
- fxStr file;
- FILE *fp = openAFMFile(file);
- if (fp == NULL) {
- emsg = fxStr::format(
- "%s: Can not open font metrics file; using fixed widths",
- (const char*) file);
- loadFixedMetrics(625*ps/1000L); // NB: use fixed width metrics
- return (false);
- }
- /*
- * Since many ISO-encoded fonts don't include metrics for
- * the higher-order characters we cheat here and use a
- * default metric for those glyphs that were unspecified.
- */
- loadFixedMetrics(useISO8859 ? 625*ps/1000L : 0);
- char buf[1024];
- u_int lineno = 0;
- do {
- if (!getAFMLine(fp, buf, sizeof (buf))) {
- emsg = fxStr::format(
- "%s: No glyph metric table located; using fixed widths",
- (const char*) file);
- fclose(fp);
- /*
- * Next line added by Leif Erlingsson <leif@lege.com> because
- * otherwise, if the metrics-file has no glyph metric table
- * and useISO8859 == False, the FixedMetrics will be all 0's!
- * (I don't know if this does or does not cause any problem.)
- */
- loadFixedMetrics(625*ps/1000L); // NB: use fixed width metrics
- return (false);
- }
- lineno++;
- } while (strncmp(buf, "StartCharMetrics", 16));
- while (getAFMLine(fp, buf, sizeof (buf)) && strcmp(buf, "EndCharMetrics")) {
- lineno++;
- int ix, w;
- /* read the glyph position and width */
- if (sscanf(buf, "C %d ; WX %d ;", &ix, &w) != 2) {
- emsg = fxStr::format("%s, line %u: format error",
- (const char*) file, lineno);
- fclose(fp);
- return (false);
- }
- if (ix == -1) // end of unencoded glyphs
- break;
- /*
- * Next if-clause added by Leif Erlingsson <leif@lege.com> because
- * experience has shown most glyph metric table's to be useless
- * for obtaining character widths of iso8859-1 characters > 127.
- * The Adobe Helvetica-Oblique metrics-file created Tue Apr 1
- * 12:54:09 PST 1986 caused bad spacing for eight-bit iso-8859-1
- * characters, for example.
- */
- if (ix > 127)
- w = 625; // distrust metrics-file for char > 127
- if ((unsigned)ix < NCHARS)
- widths[ix] = w*ps/1000L;
- }
- fclose(fp);
- return (true);
- }