TextFormat.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:39k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: TextFormat.c++,v 1.7 2009/07/29 01:40:06 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1993-1996 Sam Leffler
  4.  * Copyright (c) 1993-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. /*
  27.  * Text to PostScript conversion and formatting support.
  28.  *
  29.  * This class takes ASCII text and produces PostScript
  30.  * doing formatting using the font metric information
  31.  * for a single font.  Multi-column output, landscape and
  32.  * portrait page orientation, and various other controls
  33.  * are supported.  Page headers a la the old enscript
  34.  * program from Adobe can also be added.  This code is
  35.  * distantly related to the lptops program by Nelson Beebe.
  36.  */
  37. #include "config.h"
  38. #include "Array.h"
  39. #include "Dictionary.h"
  40. #include "PageSize.h"
  41. #include "TextFormat.h"
  42. #include "Sys.h"
  43. #include <ctype.h>
  44. #include <errno.h>
  45. #if HAS_MMAP
  46. #include <sys/mman.h>
  47. #endif
  48. #define LUNIT  (72*20) // local coord system is .05 scale
  49. #define ICVT(x) ((TextCoord)((x)*LUNIT)) // scale inches to local coordinates
  50. #define CVTI(x) (float(x)/LUNIT) // convert coords to inches
  51. inline TextCoord fxmax(TextCoord a, TextCoord b)
  52.     { return (a > b) ? a : b; }
  53. #define COLFRAC 35 // 1/fraction of col width for margin
  54. fxDECLARE_PrimArray(OfftArray, off_t)
  55. fxIMPLEMENT_PrimArray(OfftArray, off_t)
  56. fxDECLARE_StrKeyDictionary(FontDict, TextFont*)
  57. fxIMPLEMENT_StrKeyPtrValueDictionary(FontDict, TextFont*)
  58. TextFormat::TextFormat()
  59. {
  60.     output = NULL;
  61.     tf = NULL;
  62.     pageOff = new OfftArray;
  63.     firstPageNum = 1; // starting page number
  64.     column = 1; // current text column # (1..numcol)
  65.     pageNum = 1; // current page number
  66.     workStarted = false;
  67.     fonts = new FontDict;
  68.     curFont = addFont("Roman", "Courier");
  69.     TextFormat::setupConfig(); // NB: virtual
  70. }
  71. TextFormat::~TextFormat()
  72. {
  73.     for (FontDictIter iter(*fonts); iter.notDone(); iter++)
  74. delete iter.value();
  75.     delete fonts;
  76.     if (tf != NULL)
  77. fclose(tf);
  78.     tf = NULL;
  79. }
  80. void
  81. TextFormat::warning(const char* fmt ...) const
  82. {
  83.     fputs("Warning, ", stderr);
  84.     va_list ap;
  85.     va_start(ap, fmt);
  86.     vfprintf(stderr, fmt, ap);
  87.     va_end(ap);
  88.     fputs(".n", stderr);
  89. }
  90. void
  91. TextFormat::error(const char* fmt ...) const
  92. {
  93.     va_list ap;
  94.     va_start(ap, fmt);
  95.     vfprintf(stderr, fmt, ap);
  96.     va_end(ap);
  97.     fputs(".n", stderr);
  98. }
  99. void
  100. TextFormat::fatal(const char* fmt ...) const
  101. {
  102.     va_list ap;
  103.     va_start(ap, fmt);
  104.     vfprintf(stderr, fmt, ap);
  105.     va_end(ap);
  106.     fputs(".n", stderr);
  107.     exit(1);
  108. }
  109. TextFont*
  110. TextFormat::addFont(const char* name, const char* family)
  111. {
  112.     TextFont* f = new TextFont(family);
  113.     (*fonts)[name] = f;
  114.     if (workStarted) {
  115. fxStr emsg;
  116. if (!f->readMetrics(pointSize, useISO8859, emsg))
  117.     error("Font %s: %s", f->getFamily(), (const char*) emsg);
  118.     }
  119.     return (f);
  120. }
  121. const TextFont*
  122. TextFormat::getFont(const char* name) const
  123. {
  124.     return (*fonts)[name];
  125. }
  126. void TextFormat::setFont(TextFont* f) { curFont = f; }
  127. void TextFormat::setFont(const char* name) { curFont = (*fonts)[name]; }
  128. void TextFormat::setFontPath(const char* path) { TextFont::fontPath = path; }
  129. void TextFormat::setOutputFile(FILE* f) { output = f; }
  130. void TextFormat::setNumberOfColumns(u_int n) { numcol = n; }
  131. void TextFormat::setPageHeaders(bool b) { headers = b; }
  132. void TextFormat::setISO8859(bool b) { useISO8859 = b; }
  133. void TextFormat::setLineWrapping(bool b) { wrapLines = b; }
  134. void TextFormat::setOutlineMargin(TextCoord o) { outline = o; }
  135. void TextFormat::setTextPointSize(TextCoord p) { pointSize = p; }
  136. void TextFormat::setPageOrientation(u_int o) { landscape = (o == LANDSCAPE); }
  137. void TextFormat::setPageCollation(u_int c) { reverse = (c == REVERSE); }
  138. void TextFormat::setTextLineHeight(TextCoord h) { lineHeight = h; }
  139. void TextFormat::setTitle(const char* cp) { title = cp; }
  140. void TextFormat::setFilename(const char* cp) { curFile = cp; }
  141. void
  142. TextFormat::setGaudyHeaders(bool b)
  143. {
  144.     gaudy = b;
  145.     if (gaudy)
  146. headers = true;
  147. }
  148. bool
  149. TextFormat::setTextFont(const char* name)
  150. {
  151.     if (TextFont::findFont(name)) {
  152. (*fonts)["Roman"]->family = name;
  153. return (true);
  154.     } else
  155. return (false);
  156. }
  157. /*
  158.  * Parse margin syntax: l=#,r=#,t=#,b=#
  159.  */
  160. bool
  161. TextFormat::setPageMargins(const char* s)
  162. {
  163.     for (const char* cp = s; cp && cp[0];) {
  164. if (cp[1] != '=')
  165.     return (false);
  166. TextCoord v = inch(&cp[2]);
  167. switch (tolower(cp[0])) {
  168. case 'b': bm = v; break;
  169. case 'l': lm = v; break;
  170. case 'r': rm = v; break;
  171. case 't': tm = v; break;
  172. default:
  173.     return (false);
  174. }
  175. cp = strchr(cp, ',');
  176. if (cp)
  177.     cp++;
  178.     }
  179.     return (true);
  180. }
  181. void
  182. TextFormat::setPageMargins(TextCoord l, TextCoord r, TextCoord b, TextCoord t)
  183. {
  184.     lm = l;
  185.     rm = r;
  186.     bm = b;
  187.     tm = t;
  188. }
  189. bool
  190. TextFormat::setPageSize(const char* name)
  191. {
  192.     PageSizeInfo* info = PageSizeInfo::getPageSizeByName(name);
  193.     if (info) {
  194. setPageWidth(info->width() / 25.4);
  195. setPageHeight(info->height() / 25.4);
  196. delete info;
  197. return (true);
  198.     } else
  199. return (false);
  200. }
  201. void TextFormat::setPageWidth(float pw) { physPageWidth = pw; }
  202. void TextFormat::setPageHeight(float ph) { physPageHeight = ph; }
  203. void
  204. TextFormat::setModTimeAndDate(time_t t)
  205. {
  206.     struct tm* tm = localtime(&t);
  207.     char buf[30];
  208.     strftime(buf, sizeof (buf), "%X", tm); modTime = buf;
  209.     strftime(buf, sizeof (buf), "%F", tm); modDate = buf;
  210. }
  211. void TextFormat::setModTime(const char* cp) { modTime = cp; }
  212. void TextFormat::setModDate(const char* cp) { modDate = cp; }
  213. void
  214. TextFormat::beginFormatting(FILE* o)
  215. {
  216.     output = o;
  217.     pageHeight = ICVT(physPageHeight);
  218.     pageWidth = ICVT(physPageWidth);
  219.     /*
  220.      * Open the file w+ so that we can reread the temp file.
  221.      */
  222.     tf = Sys::tmpfile();
  223.     if (tf == NULL)
  224. fatal("Cannot open temporary file: %s", strerror(errno));
  225.     numcol = fxmax(1,numcol);
  226.     if (pointSize == -1)
  227. pointSize = inch(numcol > 1 ? "7bp" : "10bp");
  228.     else
  229. pointSize = fxmax(inch("3bp"), pointSize);
  230.     if (pointSize > inch("18bp"))
  231. warning("point size is unusually large (>18pt)");
  232.     // read font metrics
  233.     for (FontDictIter iter(*fonts); iter.notDone(); iter++) {
  234. fxStr emsg;
  235. TextFont* f = iter.value();
  236. if (!f->readMetrics(pointSize, useISO8859, emsg))
  237.     error("Font %s: %s", f->getFamily(), (const char*) emsg);
  238.     }
  239.     outline = fxmax(0L,outline);
  240.     curFont = (*fonts)["Roman"];
  241.     tabWidth = tabStop * curFont->charwidth(' ');
  242.     if (landscape) {
  243. TextCoord t = pageWidth;
  244. pageWidth = pageHeight;
  245. pageHeight = t;
  246.     }
  247.     if (lm+rm >= pageWidth)
  248. fatal("Margin values too large for page; lm %lu rm %lu page width %lu",
  249.     lm, rm, pageWidth);
  250.     if (tm+bm >= pageHeight)
  251. fatal("Margin values too large for page; tm %lu bm %lu page height %lu",
  252.     tm, bm, pageHeight);
  253.     col_width = (pageWidth - (lm + rm))/numcol;
  254.     if (numcol > 1 || outline)
  255. col_margin = col_width/COLFRAC;
  256.     else
  257. col_margin = 0;
  258.     /*
  259.      * TeX's baseline skip is 12pt for
  260.      * 10pt type, we preserve that ratio.
  261.      */
  262.     if (lineHeight <= 0)
  263. lineHeight = (pointSize * 12L) / 10L;
  264.     workStarted = true;
  265. }
  266. void
  267. TextFormat::endFormatting(bool skiptrailer)
  268. {
  269.     emitPrologue();
  270.     /*
  271.      * Now rewind temporary file and write
  272.      * pages to stdout in appropriate order.
  273.      */
  274.     if (reverse) {
  275. rewind(tf);
  276. off_t last = (*pageOff)[pageOff->length()-1];
  277. for (int k = pageNum-firstPageNum; k >= 0; --k) {
  278.     /* copy remainder in reverse order */
  279.     off_t next = (off_t) ftell(stdout);
  280.     Copy_Block((*pageOff)[k],last-1);
  281.     last = (*pageOff)[k];
  282.     (*pageOff)[k] = next;
  283. }
  284.     } else {
  285. off_t last = ftell(tf);
  286. rewind(tf);
  287. Copy_Block(0L, last-1);
  288.     }
  289.     if (fclose(tf))
  290. fatal("Close failure on temporary file: %s", strerror(errno));
  291.     tf = NULL;
  292.     if (!skiptrailer) emitTrailer();
  293.     fflush(output);
  294.     workStarted = false;
  295. }
  296. /* copy bytes b1..b2 to stdout */
  297. void
  298. TextFormat::Copy_Block(off_t b1, off_t b2)
  299. {
  300.     char buf[16*1024];
  301.     for (off_t k = b1; k <= b2; k += sizeof (buf)) {
  302. size_t cc = (size_t) fxmin(sizeof (buf), (size_t) (b2-k+1));
  303. fseek(tf, (long) k, SEEK_SET); // position to desired block
  304. if (fread(buf, 1, (size_t) cc, tf) != cc)
  305.     fatal("Read error during reverse collation: %s", strerror(errno));
  306. if (fwrite(buf, 1, (size_t) cc, output) != cc)
  307.     fatal("Output write error: %s", strerror(errno));
  308.     }
  309. }
  310. static const char* ISOprologue2 = "
  311. /reencodeISO{n
  312.   dup length dict beginn
  313.     {1 index /FID ne {def}{pop pop} ifelse} foralln
  314.     /Encoding ISOLatin1Encoding defn
  315.     currentdictn
  316.   endn
  317. }defn
  318. /findISO{n
  319.   dup /FontType known{n
  320.     dup /FontType get 3 nen
  321.     1 index /CharStrings known{n
  322.       1 index /CharStrings get /Thorn knownn
  323.     }{false}ifelsen
  324.     andn
  325.   }{false}ifelsen
  326. }defn
  327. ";
  328. /*
  329.  * Yech, instead of a single string that we fputs, we
  330.  * break things up into smaller chunks to satisfy braindead
  331.  * compilers...
  332.  */
  333. void
  334. TextFormat::putISOPrologue(void)
  335. {
  336.     fputs("/ISOLatin1Encoding where{pop save true}{false}ifelsen", output);
  337.     fputs("/ISOLatin1Encoding[n", output);
  338.     fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
  339.     fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
  340.     fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
  341.     fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
  342.     fputs(" /.notdef /.notdef /.notdef /.notdef /.notdef /.notdefn", output);
  343.     fputs(" /.notdef /.notdef /space /exclam /quotedbl /numbersignn", output);
  344.     fputs(" /dollar /percent /ampersand /quoteright /parenleftn", output);
  345.     fputs(" /parenright /asterisk /plus /comma /minus /periodn", output);
  346.     fputs(" /slash /zero /one /two /three /four /five /six /sevenn", output);
  347.     fputs(" /eight /nine /colon /semicolon /less /equal /greatern", output);
  348.     fputs(" /question /at /A /B /C /D /E /F /G /H /I /J /K /L /Mn", output);
  349.     fputs(" /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleftn", output);
  350.     fputs(" /backslash /bracketright /asciicircum /underscoren", output);
  351.     fputs(" /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /mn", output);
  352.     fputs(" /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleftn", output);
  353.     fputs(" /bar /braceright /asciitilde /guilsinglright /fractionn", output);
  354.     fputs(" /florin /quotesingle /quotedblleft /guilsinglleft /fin", output);
  355.     fputs(" /fl /endash /dagger /daggerdbl /bullet /quotesinglbasen", output);
  356.     fputs(" /quotedblbase /quotedblright /ellipsis /trademarkn", output);
  357.     fputs(" /perthousand /grave /scaron /circumflex /Scaron /tilden", output);
  358.     fputs(" /breve /zcaron /dotaccent /dotlessi /Zcaron /ringn", output);
  359.     fputs(" /hungarumlaut /ogonek /caron /emdash /space /exclamdownn", output);
  360.     fputs(" /cent /sterling /currency /yen /brokenbar /sectionn", output);
  361.     fputs(" /dieresis /copyright /ordfeminine /guillemotleftn", output);
  362.     fputs(" /logicalnot /hyphen /registered /macron /degreen", output);
  363.     fputs(" /plusminus /twosuperior /threesuperior /acute /mun", output);
  364.     fputs(" /paragraph /periodcentered /cedilla /onesuperiorn", output);
  365.     fputs(" /ordmasculine /guillemotright /onequarter /onehalfn", output);
  366.     fputs(" /threequarters /questiondown /Agrave /Aacuten", output);
  367.     fputs(" /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedillan", output);
  368.     fputs(" /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacuten", output);
  369.     fputs(" /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacuten", output);
  370.     fputs(" /Ocircumflex /Otilde /Odieresis /multiply /Oslashn", output);
  371.     fputs(" /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thornn", output);
  372.     fputs(" /germandbls /agrave /aacute /acircumflex /atilden", output);
  373.     fputs(" /adieresis /aring /ae /ccedilla /egrave /eacuten", output);
  374.     fputs(" /ecircumflex /edieresis /igrave /iacute /icircumflexn", output);
  375.     fputs(" /idieresis /eth /ntilde /ograve /oacute /ocircumflexn", output);
  376.     fputs(" /otilde /odieresis /divide /oslash /ugrave /uacuten", output);
  377.     fputs(" /ucircumflex /udieresis /yacute /thorn /ydieresisn", output);
  378.     fputs("]def{restore}ifn", output);
  379.     fputs(ISOprologue2, output);
  380. }
  381. static const char* defPrologue = "
  382. /Cols %u defn
  383. /PageWidth %.2f defn
  384. /PageHeight %.2f defn
  385. /LH %u defn
  386. /B{gsave}defn
  387. /LN{show}defn
  388. /EL{grestore 0 -%d rmoveto}defn
  389. /M{0 rmoveto}defn
  390. /O{gsave show grestore}defn
  391. /LandScape{90 rotate 0 -%ld translate}defn
  392. /U{%d mul}defn
  393. /UP{U 72 div}defn
  394. /S{show grestore 0 -%d rmoveto}defn
  395. ";
  396. static const char* headerPrologue1 = "
  397. /InitGaudyHeaders{n
  398.   /HeaderY exch def /BarLength exch defn
  399.   /ftD /Times-Bold findfont 12 UP scalefont defn
  400.   /ftF /Times-Roman findfont 14 UP scalefont defn
  401.   /ftP /Helvetica-Bold findfont 30 UP scalefont defn
  402.   /fillbox{ % w h x y => -n
  403.     moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineton
  404.     closepath filln
  405.   }defn
  406.   /LB{ % x y w h (label) font labelColor boxColor labelPtSize => -n
  407.     gsaven
  408.     /pts exch UP def /charcolor exch def /boxcolor exch defn
  409.     /font exch def /label exch defn
  410.     /h exch def /w exch defn
  411.     /y exch def /x exch defn
  412.     boxcolor setgray w h x y fillboxn
  413.     /lines label length defn
  414.     /ly y h add h lines pts mul sub 2 div sub pts .85 mul sub defn
  415.     font setfont charcolor setgrayn
  416.     label {n
  417.       dup stringwidth popn
  418.       2 div x w 2 div add exch sub ly moveton
  419.       shown
  420.       /ly ly pts sub defn
  421.     } foralln
  422.     grestoren
  423.   }defn
  424.   /Header{ % (file) [(date)] (page) => -n
  425.     /Page exch def /Date exch def /File exch defn
  426.     .25 U HeaderY U BarLength .1 sub U .25 U [File] ftF .97 0 14 LBn
  427.     .25 U HeaderY .25 add U BarLength .1 sub U .25 U [()] ftF 1 0 14 LBn
  428.     .25 U HeaderY U 1 U .5 U Date ftD .7 0 12 LBn
  429.     BarLength .75 sub U HeaderY U 1 U .5 U [Page] ftP .7 1 30 LBn
  430.     1 1 Cols 1 sub{n
  431.       BarLength Cols div mul .19 add U HeaderY U moveto 0 -10 U rlineto stroken
  432.     }forn
  433.   }defn
  434. }defn
  435. ";
  436. static const char* headerPrologue2 = "
  437. /InitNormalHeaders{n
  438.   /HeaderY exch def /BarLength exch defn
  439.   /ftF /Times-Roman findfont 14 UP scalefont defn
  440.   /ftP /Helvetica-Bold findfont 14 UP scalefont defn
  441.   /LB{ % x y w h (label) font labelColor labelPtSize => -n
  442.     gsaven
  443.     /pts exch UP def /charcolor exch defn
  444.     /font exch def /label exch defn
  445.     /h exch def /w exch defn
  446.     /y exch def /x exch defn
  447.     /ly y h add h pts sub 2 div sub pts .85 mul sub defn
  448.     font setfont charcolor setgrayn
  449.     label stringwidth pop 2 div x w 2 div add exch sub ly moveton
  450.     label shown
  451.     grestoren
  452.   }defn
  453.   /Header{ % (file) [(date)] (page) => -n
  454.     /Page exch def pop /File exch defn
  455.     .25 U HeaderY U BarLength 2 div U .5 U File ftF 0 14 LBn
  456.     BarLength .75 sub U HeaderY U 1 U .5 U Page ftP 0 14 LBn
  457.     1 1 Cols 1 sub{n
  458.       BarLength Cols div mul .19 add U HeaderY U moveto 0 -10 U rlineto stroken
  459.     }forn
  460.   }defn
  461. }defn
  462. /InitNullHeaders{/Header{3{pop}repeat}def pop pop}defn
  463. ";
  464. /*
  465.  * Emit the DSC header comments and prologue.
  466.  */
  467. void
  468. TextFormat::emitPrologue(void)
  469. {
  470.     fputs("%!PS-Adobe-3.0n", output);
  471.     fprintf(output, "%%%%Creator: HylaFAX TextFormat Classn");
  472.     fprintf(output, "%%%%Title: %sn", (const char*) title);
  473.     time_t t = Sys::now();
  474.     fprintf(output, "%%%%CreationDate: %s", ctime(&t));
  475.     char* cp = getlogin();
  476.     fprintf(output, "%%%%For: %sn", cp ? cp : "");
  477.     fputs("%%Origin: 0 0n", output);
  478.     fprintf(output, "%%%%BoundingBox: 0 0 %.0f %.0fn",
  479. physPageWidth*72, physPageHeight*72);
  480.     fputs("%%Pages: (atend)n", output);
  481.     fprintf(output, "%%%%PageOrder: %sn",
  482. reverse ? "Descend" : "Ascend");
  483.     fprintf(output, "%%%%Orientation: %sn",
  484. landscape ? "Landscape" : "Portrait");
  485.     fprintf(output, "%%%%DocumentNeededResources: font");
  486.     FontDictIter iter;
  487.     for (iter = *fonts; iter.notDone(); iter++) {
  488. TextFont* f = iter.value();
  489. fprintf(output, " %s", f->getFamily());
  490.     }
  491.     fputc('n', output);
  492.     if (gaudy) {
  493. fputs("%%+ font Times-Boldn", output);
  494. fputs("%%+ font Times-Romann", output);
  495. fputs("%%+ font Helvetica-Boldn", output);
  496.     }
  497.     emitClientComments(output);
  498.     fprintf(output, "%%%%EndCommentsn");
  499.     fprintf(output, "%%%%BeginPrologn");
  500.     fputs("/$printdict 50 dict def $printdict beginn", output);
  501.     if (useISO8859)
  502. putISOPrologue();
  503.     fprintf(output, defPrologue
  504. , numcol
  505. , CVTI(pageWidth - (lm+rm))
  506. , CVTI(pageHeight - (tm+bm))
  507. , lineHeight
  508. , lineHeight
  509. , pageHeight
  510. , LUNIT
  511. , lineHeight
  512.     );
  513.     fputs(headerPrologue1, output);
  514.     fputs(headerPrologue2, output);
  515.     fprintf(output, "%.2f %.2f Init%sHeadersn"
  516. , CVTI(pageWidth - (lm+rm))
  517. , CVTI(pageHeight - tm)
  518. , (gaudy ? "Gaudy" : headers ? "Normal" : "Null")
  519.     );
  520.     for (iter = *fonts; iter.notDone(); iter++)
  521. iter.value()->defFont(output, pointSize, useISO8859);
  522.     emitClientPrologue(output);
  523.     fputs("endn", output);
  524.     fputs("%%EndPrologn", output);
  525. }
  526. void TextFormat::emitClientComments(FILE*) {}
  527. void TextFormat::emitClientPrologue(FILE*) {}
  528. /*
  529.  * Emit the DSC trailer comments.
  530.  */
  531. void
  532. TextFormat::emitTrailer(void)
  533. {
  534.     fputs("%%Trailern", output);
  535.     fprintf(output, "%%%%Pages: %dn", pageNum - firstPageNum);
  536.     fputs("%%EOFn", output);
  537. }
  538. void
  539. TextFormat::newPage(void)
  540. {
  541.     x = lm; // x starts at left margin
  542.     right_x = col_width - col_margin/2; // right x, 0-relative
  543.     y = pageHeight - tm - lineHeight; // y at page top
  544.     level = 0; // string paren level reset
  545.     column = 1;
  546.     boc = true;
  547.     bop = true;
  548. }
  549. void
  550. TextFormat::newCol(void)
  551. {
  552.     x += col_width; // x, shifted
  553.     right_x += col_width; // right x, shifted
  554.     y = pageHeight - tm - lineHeight; // y at page top
  555.     level = 0;
  556.     column++;
  557.     boc = true;
  558. }
  559. static void
  560. putString(FILE* fd, const char* val)
  561. {
  562.     fputc('(', fd);
  563.     for (; *val; val++) {
  564. u_int c = *val & 0xff;
  565. if ((c & 0200) == 0) {
  566.     if (c == '(' || c == ')' || c == '\')
  567. fputc('\', fd);
  568.     fputc(c, fd);
  569. } else
  570.     fprintf(fd, "\%03o", c);
  571.     }
  572.     fputc(')', fd);
  573. }
  574. void
  575. TextFormat::beginCol(void)
  576. {
  577.     if (column == 1) { // new page
  578. if (reverse)  {
  579.     u_int k = pageNum-firstPageNum;
  580.     off_t off = (off_t) ftell(tf);
  581.     if (k < pageOff->length())
  582. (*pageOff)[k] = off;
  583.     else
  584. pageOff->append(off);
  585. }
  586. fprintf(tf,"%%%%Page: "%d" %dn", pageNum-firstPageNum+1, pageNum);
  587. fputs("save $printdict beginn", tf);
  588. fprintf(tf, ".05 dup scalen");
  589. curFont->setfont(tf);
  590. if (landscape)
  591.     fputs("LandScapen", tf);
  592. putString(tf, curFile);
  593. fputc('[', tf);
  594. putString(tf, modDate);
  595. putString(tf, modTime);
  596. fputc(']', tf);
  597. fprintf(tf, "(%d)Headern", pageNum);
  598.     }
  599.     fprintf(tf, "%ld %ld moveton",x,y);
  600. }
  601. void
  602. TextFormat::beginLine(void)
  603. {
  604.     if (boc)
  605. beginCol(), boc = false, bop = false;
  606.     fputc('B', tf);
  607. }
  608. void
  609. TextFormat::beginText(void)
  610. {
  611.     fputc('(', tf);
  612.     level++;
  613. }
  614. void
  615. TextFormat::hrMove(TextCoord x)
  616. {
  617.     fprintf(tf, " %ld M ", x);
  618.     xoff += x;
  619. }
  620. void
  621. TextFormat::closeStrings(const char* cmd)
  622. {
  623.     int l = level;
  624.     for (; level > 0; level--)
  625. fputc(')', tf);
  626.     if (l > 0)
  627. fputs(cmd, tf);
  628. }
  629. void
  630. TextFormat::beginFile(void)
  631. {
  632.     newPage(); // each file starts on a new page
  633.     bol = true; // force line start
  634.     bot = true; // force text start
  635.     xoff = col_width * (column-1);
  636. }
  637. void
  638. TextFormat::endFile(void)
  639. {
  640.     if (!bot)
  641. endTextLine();
  642.     if (!bol)
  643. endLine();
  644.     if (!bop) {
  645. column = numcol; // force page end action
  646. endTextCol();
  647.     }
  648.     if (reverse) {
  649. /*
  650.  * Normally, beginCol sets the pageOff entry.  Since this is
  651.  * the last output for this file, we must set it here manually.
  652.  * If more files are printed, this entry will be overwritten (with
  653.  * the same value) at the next call to beginCol.
  654.  */
  655. off_t off = (off_t) ftell(tf);
  656. pageOff->append(off);
  657.     }
  658. }
  659. void
  660. TextFormat::formatFile(const char* name)
  661. {
  662.     FILE* fp = fopen(name, "r");
  663.     if (fp != NULL) {
  664. curFile = name;
  665. formatFile(fp);
  666. fclose(fp);
  667.     } else
  668. error("%s: Cannot open file: %s", name, strerror(errno));
  669. }
  670. void
  671. TextFormat::formatFile(FILE* fp)
  672. {
  673. #if HAS_MMAP
  674.     struct stat sb;
  675.     Sys::fstat(fileno(fp), sb);
  676.     char* addr = (char*)
  677. mmap(NULL, (size_t) sb.st_size, PROT_READ, MAP_SHARED, fileno(fp), 0);
  678.     if (addr == (char*) -1) { // revert to file reads
  679. #endif
  680. int c;
  681. while ((c = getc(fp)) == 'f') // discard initial form feeds
  682.     ;
  683. ungetc(c, fp);
  684. beginFile();
  685. format(fp);
  686. endFile();
  687. #if HAS_MMAP
  688.     } else {
  689. const char* cp = addr;
  690. const char* ep = cp + sb.st_size;
  691. while (cp < ep && *cp == 'f') // discard initial form feeds
  692.     cp++;
  693. beginFile();
  694. format(cp, ep-cp);
  695. endFile();
  696. munmap(addr, (size_t) sb.st_size);
  697.     }
  698. #endif
  699. }
  700. void
  701. TextFormat::format(FILE* fp)
  702. {
  703.     int c;
  704.     while ((c = getc(fp)) != EOF) {
  705. switch (c) {
  706. case '': // discard nulls
  707.     break;
  708. case 'f': // form feed
  709.     if (!bop) {
  710. endTextCol();
  711. bol = bot = true;
  712.     }
  713.     break;
  714. case 'n': // line break
  715.     if (bol)
  716. beginLine();
  717.     if (bot)
  718. beginText();
  719.     endTextLine();
  720.     break;
  721. case 'r': // check for overstriking
  722.     if ((c = getc(fp)) == 'n') {
  723. ungetc(c,fp); // collapse rn => n
  724. break;
  725.     }
  726.     closeStrings("On"); // do overstriking
  727.     bot = true; // start new string
  728.     break;
  729. default:
  730.     TextCoord hm;
  731.     if (c == 't' || c == ' ') {
  732. /*
  733.  * Coalesce white space into one relative motion.
  734.  * The offset calculation below is to insure that
  735.  * 0 means the start of the line (no matter what
  736.  * column we're in).
  737.  */
  738. hm = 0;
  739. int cc = c;
  740. TextCoord off = xoff - col_width*(column-1);
  741. do {
  742.     if (cc == 't')
  743. hm += tabWidth - (off+hm) % tabWidth;
  744.     else
  745. hm += curFont->charwidth(' ');
  746. } while ((cc = getc(fp)) == 't' || cc == ' ');
  747. if (cc != EOF)
  748.     ungetc(cc, fp);
  749. /*
  750.  * If the motion is one space's worth, either
  751.  * a single blank or a tab that causes a blank's
  752.  * worth of horizontal motion, then force it
  753.  * to be treated as a blank below.
  754.  */
  755. if (hm == curFont->charwidth(' '))
  756.     c = ' ';
  757. else
  758.     c = 't';
  759.     } else
  760. hm = curFont->charwidth(c);
  761.     if (xoff + hm > right_x) {
  762. if (!wrapLines) // discard line overflow
  763.     break;
  764. if (c == 't') // adjust white space motion
  765.     hm -= right_x - xoff;
  766. endTextLine();
  767.     }
  768.     if (bol)
  769. beginLine(), bol = false;
  770.     if (c == 't') { // close open PS string and do motion
  771. if (hm > 0) {
  772.     closeStrings("LN");
  773.     bot = true; // force new string
  774.     hrMove(hm);
  775. }
  776.     } else { // append to open PS string
  777. if (bot)
  778.     beginText(), bot = false;
  779. if (040 <= c && c <= 0176) {
  780.     if (c == '(' || c == ')' || c == '\')
  781. fputc('\',tf);
  782.     fputc(c,tf);
  783. } else
  784.     fprintf(tf, "\%03o", c & 0xff);
  785. xoff += hm;
  786.     }
  787.     break;
  788. }
  789.     }
  790. }
  791. void
  792. TextFormat::format(const char* cp, u_int cc)
  793. {
  794.     const char* ep = cp+cc;
  795.     while (cp < ep) {
  796. int c = *cp++ & 0xff;
  797. switch (c) {
  798. case '': // discard nulls
  799.     break;
  800. case 'f': // form feed
  801.     if (!bop) {
  802. endTextCol();
  803. bol = bot = true;
  804.     }
  805.     break;
  806. case 'n': // line break
  807.     if (bol)
  808. beginLine();
  809.     if (bot)
  810. beginText();
  811.     endTextLine();
  812.     break;
  813. case 'r': // check for overstriking
  814.     if (cp < ep && *cp == 'n')
  815. break; // collapse rn => n
  816.     cp++; // count character
  817.     closeStrings("On"); // do overstriking
  818.     bot = true; // start new string
  819.     break;
  820. default:
  821.     TextCoord hm;
  822.     if (c == 't' || c == ' ') {
  823. /*
  824.  * Coalesce white space into one relative motion.
  825.  * The offset calculation below is to insure that
  826.  * 0 means the start of the line (no matter what
  827.  * column we're in).
  828.  */
  829. hm = 0;
  830. int cc = c;
  831. TextCoord off = xoff - col_width*(column-1);
  832. do {
  833.     if (cc == 't')
  834. hm += tabWidth - (off+hm) % tabWidth;
  835.     else
  836. hm += curFont->charwidth(' ');
  837. } while (cp < ep && ((cc = *cp++) == 't' || cc == ' '));
  838. if (cc != 't' && cc != ' ')
  839.     cp--;
  840. /*
  841.  * If the motion is one space's worth, either
  842.  * a single blank or a tab that causes a blank's
  843.  * worth of horizontal motion, then force it
  844.  * to be treated as a blank below.
  845.  */
  846. if (hm == curFont->charwidth(' '))
  847.     c = ' ';
  848. else
  849.     c = 't';
  850.     } else
  851. hm = curFont->charwidth(c);
  852.     if (xoff + hm > right_x) {
  853. if (!wrapLines) // discard line overflow
  854.     break;
  855. if (c == 't') // adjust white space motion
  856.     hm -= right_x - xoff;
  857. endTextLine();
  858.     }
  859.     if (bol)
  860. beginLine(), bol = false;
  861.     if (c == 't') { // close open PS string and do motion
  862. if (hm > 0) {
  863.     closeStrings("LN");
  864.     fprintf(tf, " %ld M ", hm);
  865.     bot = true; // force new string
  866. }
  867.     } else { // append to open PS string
  868. if (bot)
  869.     beginText(), bot = false;
  870. if (040 <= c && c <= 0176) {
  871.     if (c == '(' || c == ')' || c == '\')
  872. fputc('\',tf);
  873.     fputc(c,tf);
  874. } else
  875.     fprintf(tf, "\%03o", c & 0xff);
  876.     }
  877.     xoff += hm;
  878.     break;
  879. }
  880.     }
  881. }
  882. static const char* outlineCol = "n
  883. gsave
  884.     %ld setlinewidth
  885.     newpath %ld %ld moveto
  886.     %ld %ld rlineto
  887.     %ld %ld rlineto
  888.     %ld %ld rlineto
  889.     closepath stroke 
  890. grestoren
  891. ";
  892. void
  893. TextFormat::endCol(void)
  894. {
  895.     if (outline > 0) {
  896. fprintf(tf, outlineCol, outline,
  897.     x - col_margin, bm, col_width, 0, 0,
  898.     pageHeight-bm-tm, -col_width, 0);
  899.     }
  900.     if (column == numcol) { // columns filled, start new page
  901. if (!bop) {
  902.     pageNum++;
  903.     fputs("showpagenend restoren", tf);
  904.     flush();
  905.     newPage();
  906. }
  907.     } else
  908. if (!boc)
  909.     newCol();
  910. }
  911. void
  912. TextFormat::endPage(void)
  913. {
  914.     column = numcol;
  915.     endCol();
  916. }
  917. void
  918. TextFormat::endLine(void)
  919. {
  920.     fputs("ELn", tf);
  921.     if ((y -= lineHeight) < bm)
  922. endCol();
  923.     xoff = col_width * (column-1);
  924. }
  925. void
  926. TextFormat::endTextCol(void)
  927. {
  928.     closeStrings("LN");
  929.     fputc('n', tf);
  930.     endCol();
  931. }
  932. void
  933. TextFormat::endTextLine(void)
  934. {
  935.     closeStrings("Sn");
  936.     if ((y -= lineHeight) < bm)
  937. endCol();
  938.     xoff = col_width * (column-1);
  939.     bol = bot = true;
  940. }
  941. void
  942. TextFormat::reserveVSpace(TextCoord vs)
  943. {
  944.     if (y - vs < bm)
  945. endCol();
  946. }
  947. void
  948. TextFormat::flush(void)
  949. {
  950.     fflush(tf);
  951.     if (ferror(tf) && errno == ENOSPC)
  952. fatal("Output write error: %s", strerror(errno));
  953. }
  954. /*
  955.  * Convert a value of the form:
  956.  *  #.##bp big point (1in = 72bp)
  957.  *  #.##cc cicero (1cc = 12dd)
  958.  *  #.##cm centimeter
  959.  *  #.##dd didot point (1157dd = 1238pt)
  960.  *  #.##in inch
  961.  *  #.##mm millimeter (10mm = 1cm)
  962.  *  #.##pc pica (1pc = 12pt)
  963.  *  #.##pt point (72.27pt = 1in)
  964.  *  #.##sp scaled point (65536sp = 1pt)
  965.  * to inches, returning it as the function value.  The case of
  966.  * the dimension name is ignored.  No space is permitted between
  967.  * the number and the dimension.
  968.  */
  969. TextCoord
  970. TextFormat::inch(const char* s)
  971. {
  972.     char* cp;
  973.     double v = strtod(s, &cp);
  974.     if (cp == NULL)
  975. return (ICVT(0)); // XXX
  976.     if (strncasecmp(cp,"in",2) == 0) // inches
  977. ;
  978.     else if (strncasecmp(cp,"cm",2) == 0) // centimeters
  979. v /= 2.54;
  980.     else if (strncasecmp(cp,"pt",2) == 0) // points
  981. v /= 72.27;
  982.     else if (strncasecmp(cp,"cc",2) == 0) // cicero
  983. v *= 12.0 * (1238.0 / 1157.0) / 72.27;
  984.     else if (strncasecmp(cp,"dd",2) == 0) // didot points
  985. v *= (1238.0 / 1157.0) / 72.27;
  986.     else if (strncasecmp(cp,"mm",2) == 0) // millimeters
  987. v /= 25.4;
  988.     else if (strncasecmp(cp,"pc",2) == 0) // pica
  989. v *= 12.0 / 72.27;
  990.     else if (strncasecmp(cp,"sp",2) == 0) // scaled points
  991. v /= (65536.0 * 72.27);
  992.     else // big points
  993. v /= 72.0;
  994.     return ICVT(v);
  995. }
  996. /*
  997.  * Configuration file support.
  998.  */
  999. void
  1000. TextFormat::setupConfig()
  1001. {
  1002.     gaudy = false; // emit gaudy headers
  1003.     landscape = false; // horizontal landscape mode output
  1004.     useISO8859 = true; // use the ISO 8859-1 character encoding
  1005.     reverse = false; // page reversal flag
  1006.     wrapLines = true; // wrap/truncate lines
  1007.     headers = true; // emit page headers
  1008.     pointSize = -1; // font point size in big points
  1009.     lm = inch("0.25in"); // left margin
  1010.     rm = inch("0.25in"); // right margin
  1011.     tm = inch("0.85in"); // top margin
  1012.     bm = inch("0.5in"); // bottom margin
  1013.     lineHeight = 0; // inter-line spacing
  1014.     numcol = 1; // number of text columns
  1015.     col_margin = 0L; // inter-column margin
  1016.     outline = 0L; // page and column outline linewidth
  1017.     tabStop = 8; // 8-column tab stop
  1018.     setPageSize("default"); // default system page dimensions
  1019. }
  1020. void
  1021. TextFormat::resetConfig()
  1022. {
  1023.     setupConfig();
  1024. }
  1025. void TextFormat::configError(const char* ...) {}
  1026. void TextFormat::configTrace(const char* ...) {}
  1027. #undef streq
  1028. #define streq(a,b) (strcasecmp(a,b)==0)
  1029. bool
  1030. TextFormat::setConfigItem(const char* tag, const char* value)
  1031. {
  1032.     if (streq(tag, "columns"))
  1033. setNumberOfColumns(getNumber(value));
  1034.     else if (streq(tag, "pageheaders"))
  1035. setPageHeaders(getBoolean(value));
  1036.     else if (streq(tag, "linewrap"))
  1037. setLineWrapping(getBoolean(value));
  1038.     else if (streq(tag, "iso8859"))
  1039. setISO8859(getBoolean(value));
  1040.     else if (streq(tag, "textfont"))
  1041. setTextFont(value);
  1042.     else if (streq(tag, "gaudyheaders"))
  1043. setGaudyHeaders(getBoolean(value));
  1044.     else if (streq(tag, "pagemargins"))
  1045. setPageMargins(value);
  1046.     else if (streq(tag, "outlinemargin"))
  1047. setOutlineMargin(inch(value));
  1048.     else if (streq(tag, "textpointsize"))
  1049. setTextPointSize(inch(value));
  1050.     else if (streq(tag, "orientation"))
  1051. setPageOrientation(streq(value, "landscape") ? LANDSCAPE : PORTRAIT);
  1052.     else if (streq(tag, "pagesize"))
  1053. setPageSize(value);
  1054.     else if (streq(tag, "pagewidth"))
  1055. setPageWidth(atof(value));
  1056.     else if (streq(tag, "pageheight"))
  1057. setPageHeight(atof(value));
  1058.     else if (streq(tag, "pagecollation"))
  1059. setPageCollation(streq(value, "forward") ? FORWARD : REVERSE);
  1060.     else if (streq(tag, "textlineheight"))
  1061. setTextLineHeight(inch(value));
  1062.     else if (streq(tag, "tabstop"))
  1063. tabStop = getNumber(value);
  1064.     else if (streq(tag, "fontmap")) // XXX
  1065. TextFont::fontMap = value;
  1066.     else if (streq(tag, "fontpath"))
  1067. setFontPath(value);
  1068.     else
  1069. return (false);
  1070.     return (true);
  1071. }
  1072. #define NCHARS (sizeof (widths) / sizeof (widths[0]))
  1073. fxStr TextFont::fontMap = _PATH_FONTMAP;
  1074. fxStr TextFont::fontPath = _PATH_AFM;
  1075. u_int TextFont::fontID = 0;
  1076. TextFont::TextFont(const char* cp) : family(cp)
  1077. {
  1078.     showproc = fxStr::format("s%u", fontID);
  1079.     setproc = fxStr::format("sf%u", fontID);
  1080.     fontID++;
  1081. }
  1082. TextFont::~TextFont() {}
  1083. bool
  1084. TextFont::decodeFontName(const char* name, fxStr& filename, fxStr& emsg)
  1085. {
  1086.     struct stat junk;
  1087.     fxStr path = fontMap;
  1088.     u_int index = path.next(0, ':');
  1089.     fxStr key = name;
  1090.     while (index > 0) {
  1091.         /* Newer versions of Ghostscript use "Fontmap.GS"
  1092.          * whereas older ones omit the ".GS" extension.
  1093.          * If the Fontmap.GS file isn't available default
  1094.          * to the older convention.
  1095.          */
  1096.         filename = path.head(index) | "/" | "Fontmap.GS";
  1097.         if (stat(filename, &junk) != 0)
  1098.             filename = path.head(index) | "/" | "Fontmap";
  1099.         fxStr fontMapFile = filename;
  1100.         path.remove(0, index);
  1101.         if (path.length() > 0) path.remove(0, 1);
  1102.         FILE* fd = Sys::fopen(fontMapFile, "r");
  1103.         if (fd != NULL && fontMapFile[0] == '/') {
  1104.             char buf[1024];
  1105.     int aliascount = maxaliases;
  1106.             while (fgets(buf, sizeof(buf), fd) != NULL &&
  1107. aliascount > 0) {
  1108.                 size_t len = strcspn(buf, "%n");
  1109.                 if (len == strlen(buf)) {
  1110.             emsg = fxStr::format(
  1111.                 "Warning:%s - line too long.", (const char*)fontMapFile);
  1112.                 break;
  1113.                 }
  1114.         if (len == 0) continue;
  1115.                 *(buf + len) = '';
  1116.                 char* tmp = buf + strcspn(buf, ") t");
  1117.                 *tmp++ = '';
  1118.                 tmp += strspn(tmp, " t");
  1119.                 if (strcmp(key, buf + 1) == 0) {
  1120.                     //match - now ensure it is the last one in the file
  1121.     // for gs compatibility
  1122.                     *(tmp + strcspn(tmp, ") t;")) = '';
  1123.                     fxStr val = tmp;
  1124.                     while (fgets(buf, sizeof(buf), fd) != NULL) {
  1125.                         len = strcspn(buf, "%n");
  1126.                         *(buf + len) = '';
  1127.                         if (len == strlen(buf)) {
  1128.                     emsg = fxStr::format(
  1129.                 "Warning: %s - line too long.", (const char*) fontMapFile);
  1130.                     break;
  1131.                         }
  1132. if (len == 0) continue;
  1133.                 tmp = buf + strcspn(buf, ") t");
  1134.         *tmp++ = '';
  1135.                         tmp += strspn(tmp, " t");
  1136.                         if (strcmp(key, buf + 1) == 0) {
  1137.                             *(tmp + strcspn(tmp, ") t;")) = '';
  1138.                             val = tmp;
  1139. }
  1140.     }
  1141.                     if (val[0] == '/') {
  1142.         //alias
  1143.                 aliascount--;
  1144. val.remove(0);
  1145.         key = val;
  1146.          fseek(fd, 0L, SEEK_SET);
  1147.                     } else {
  1148.                         //real file
  1149. key = name;
  1150.                         fclose(fd);
  1151. val.remove(0);
  1152. int pos = val.next(0, '.');
  1153. val.remove(pos, val.length() - pos);
  1154. val.append(".afm");
  1155. //move through dirs looking for font
  1156. fxStr fpath = fontPath;
  1157.                         int index2 = fpath.next(0, ':');
  1158.                         filename = fpath.head(index2) | "/" | val;
  1159.                         fpath.remove(0, index2);
  1160.      if (fpath.length() > 0) fpath.remove(0, 1);
  1161. while (stat(filename, &junk) != 0 && index2 > 0) {
  1162.                             index2 = fpath.next(0, ':');
  1163.                             filename = fpath.head(index2) | "/" | val;
  1164.                             fpath.remove(0, index2);
  1165.          if (fpath.length() > 0) fpath.remove(0, 1);
  1166. }
  1167. bool result = stat(filename, &junk) ? false : true;
  1168. if (!result)
  1169.                     emsg = fxStr::format(
  1170.         "Warning: %s invalid Fontmap entry - no filename present", (const char*)val);
  1171.                         return result;
  1172.                     }
  1173.                 }
  1174.             }
  1175.             fclose(fd);
  1176.         }
  1177.         index = path.next(0, ':');
  1178.     }
  1179.     //decoding using fontmap has failed
  1180.     //now try plain filename with/without afm extension
  1181.     path = fontPath;
  1182.     index = path.next(0, ':');
  1183.     while (index > 0) {
  1184.         filename = path.head(index) | "/" | key | ".afm";
  1185.         path.remove(0, index);
  1186.         if (path.length() > 0) path.remove(0, 1);
  1187.         if (stat(filename, &junk) == 0) return true;
  1188. filename.resize(filename.length()-4); // strip ``.afm''
  1189.         if (stat(filename, &junk) == 0) return true;
  1190.         index = path.next(0, ':');
  1191.     }
  1192.     return false;
  1193. }
  1194. bool
  1195. TextFont::findFont(const char* name)
  1196. {
  1197.     fxStr myname, emsg;
  1198.     return decodeFontName(name, myname, emsg);
  1199. }
  1200. static const char* defISOFont = "
  1201. /%s{/%s findfont
  1202.   findISO{reencodeISO /%s-ISO exch definefont}if
  1203.   %d UP scalefont setfont
  1204. }defn
  1205. ";
  1206. static const char* defRegularFont = "
  1207. /%s{/%s findfont %d UP scalefont setfont}defn
  1208. ";
  1209. void
  1210. TextFont::defFont(FILE* fd, TextCoord ps, bool useISO8859) const
  1211. {
  1212.     if (useISO8859) {
  1213. fprintf(fd, defISOFont, (const char*) setproc,
  1214.     (const char*) family, (const char*) family, ps/20, ps/20); 
  1215.     } else {
  1216. fprintf(fd, defRegularFont, (const char*) setproc,
  1217.     (const char*) family, ps/20);
  1218.     }
  1219.     fprintf(fd, "/%s{%s show}defn",
  1220. (const char*) showproc, (const char*) setproc);
  1221. }
  1222. void
  1223. TextFont::setfont(FILE* fd) const
  1224. {
  1225.     fprintf(fd, " %s ", (const char*) setproc);
  1226. }
  1227. TextCoord
  1228. TextFont::show(FILE* fd, const char* val, int len) const
  1229. {
  1230.     TextCoord hm = 0;
  1231.     if (len > 0) {
  1232. fprintf(fd, "(");
  1233. do {
  1234.     u_int c = *val++ & 0xff;
  1235.     if ((c & 0200) == 0) {
  1236. if (c == '(' || c == ')' || c == '\')
  1237.     fputc('\', fd);
  1238. fputc(c, fd);
  1239.     } else
  1240. fprintf(fd, "\%03o", c);
  1241.     hm += widths[c]; // Leif Erlingsson <leif@lege.com>
  1242. } while (--len);
  1243. fprintf(fd, ")%s ", (const char*) showproc);
  1244.     }
  1245.     return (hm);
  1246. }
  1247. TextCoord
  1248. TextFont::show(FILE* fd, const fxStr& s) const
  1249. {
  1250.     return show(fd, s, s.length());
  1251. }
  1252. TextCoord
  1253. TextFont::strwidth(const char* cp) const
  1254. {
  1255.     TextCoord w = 0;
  1256.     while (*cp)
  1257. w += widths[(unsigned) (*cp++ & 0xff)]; // Leif Erlingsson <leif@lege.com>
  1258.     return w;
  1259. }
  1260. void
  1261. TextFont::loadFixedMetrics(TextCoord w)
  1262. {
  1263.     for (u_int i = 0; i < NCHARS; i++)
  1264. widths[i] = w;
  1265. }
  1266. bool
  1267. TextFont::getAFMLine(FILE* fp, char* buf, int bsize)
  1268. {
  1269.     if (fgets(buf, bsize, fp) == NULL)
  1270. return (false);
  1271.     char* cp = strchr(buf, 'n');
  1272.     if (cp == NULL) { // line too long, skip it
  1273. int c;
  1274. while ((c = getc(fp)) != 'n') // skip to end of line
  1275.     if (c == EOF)
  1276. return (false);
  1277. cp = buf; // force line to be skipped
  1278.     }
  1279.     *cp = '';
  1280.     return (true);
  1281. }
  1282. FILE*
  1283. TextFont::openAFMFile(fxStr& fontpath)
  1284. {
  1285.     fxStr emsg;
  1286.     if (!decodeFontName(family, fontpath, emsg)) {
  1287. fprintf(stderr,emsg);
  1288.         return NULL;
  1289.     } 
  1290.     return Sys::fopen(fontpath, "r");
  1291. }
  1292. bool
  1293. TextFont::readMetrics(TextCoord ps, bool useISO8859, fxStr& emsg)
  1294. {
  1295.     fxStr file;
  1296.     FILE *fp = openAFMFile(file);
  1297.     if (fp == NULL) {
  1298. emsg = fxStr::format(
  1299.     "%s: Can not open font metrics file; using fixed widths",
  1300.     (const char*) file);
  1301. loadFixedMetrics(625*ps/1000L); // NB: use fixed width metrics
  1302. return (false);
  1303.     }
  1304.     /*
  1305.      * Since many ISO-encoded fonts don't include metrics for
  1306.      * the higher-order characters we cheat here and use a
  1307.      * default metric for those glyphs that were unspecified.
  1308.      */
  1309.     loadFixedMetrics(useISO8859 ? 625*ps/1000L : 0);
  1310.     char buf[1024];
  1311.     u_int lineno = 0;
  1312.     do {
  1313. if (!getAFMLine(fp, buf, sizeof (buf))) {
  1314.     emsg = fxStr::format(
  1315. "%s: No glyph metric table located; using fixed widths",
  1316. (const char*) file);
  1317.     fclose(fp);
  1318.     /*
  1319.      * Next line added by Leif Erlingsson <leif@lege.com> because
  1320.      * otherwise, if the metrics-file has no glyph metric table
  1321.      * and useISO8859 == False, the FixedMetrics will be all 0's!
  1322.      * (I don't know if this does or does not cause any problem.)
  1323.      */
  1324.     loadFixedMetrics(625*ps/1000L); // NB: use fixed width metrics
  1325.     return (false);
  1326. }
  1327. lineno++;
  1328.     } while (strncmp(buf, "StartCharMetrics", 16));
  1329.     while (getAFMLine(fp, buf, sizeof (buf)) && strcmp(buf, "EndCharMetrics")) {
  1330. lineno++;
  1331. int ix, w;
  1332. /* read the glyph position and width */
  1333. if (sscanf(buf, "C %d ; WX %d ;", &ix, &w) != 2) {
  1334.     emsg = fxStr::format("%s, line %u: format error",
  1335. (const char*) file, lineno);
  1336.     fclose(fp);
  1337.     return (false);
  1338. }
  1339. if (ix == -1) // end of unencoded glyphs
  1340.     break;
  1341. /*
  1342.  * Next if-clause added by Leif Erlingsson <leif@lege.com> because
  1343.  * experience has shown most glyph metric table's to be useless
  1344.  * for obtaining character widths of iso8859-1 characters > 127.
  1345.  * The Adobe Helvetica-Oblique metrics-file created Tue Apr 1
  1346.  * 12:54:09 PST 1986 caused bad spacing for eight-bit iso-8859-1
  1347.  * characters, for example.
  1348.  */
  1349. if (ix > 127)
  1350.     w = 625; // distrust metrics-file for char > 127
  1351. if ((unsigned)ix < NCHARS)
  1352.     widths[ix] = w*ps/1000L;
  1353.     }
  1354.     fclose(fp);
  1355.     return (true);
  1356. }