home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1996 February / PCWK0296.iso / sharewar / dos / inne / gs300ini / bdftops.ps < prev    next >
Encoding:
Text File  |  1994-08-02  |  23.3 KB  |  779 lines

  1. %    Copyright (C) 1990, 1991, 1993, 1994 Aladdin Enterprises.  All rights reserved.
  2. % This file is part of Aladdin Ghostscript.
  3. % Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  4. % or distributor accepts any responsibility for the consequences of using it,
  5. % or for whether it serves any particular purpose or works at all, unless he
  6. % or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  7. % License (the "License") for full details.
  8. % Every copy of Aladdin Ghostscript must include a copy of the License,
  9. % normally in a plain ASCII text file named PUBLIC.  The License grants you
  10. % the right to copy, modify and redistribute Aladdin Ghostscript, but only
  11. % under certain conditions described in the License.  Among other things, the
  12. % License requires that the copyright notice and this notice be preserved on
  13. % all copies.
  14.  
  15. % bdftops.ps
  16. % Convert a BDF file (possibly with (an) associated AFM file(s))
  17. % to a PostScript Type 1 font (without eexec encryption).
  18. % The resulting font will work with any PostScript language interpreter,
  19. % but not with ATM or other font rasterizers lacking a complete interpreter.
  20.  
  21. /envBDF 120 dict def
  22. envBDF begin
  23.  
  24. % "Import" the image-to-path package.
  25. % This also brings in the Type 1 opcodes (type1ops.ps).
  26.    (impath.ps) run
  27.  
  28. % "Import" the font-writing package.
  29.    (wrfont.ps) run
  30.    wrfont_dict begin
  31.      /binary_CharStrings false def
  32.      /binary_tokens false def
  33.      /encrypt_CharStrings true def
  34.      /standard_only true def
  35.    end
  36.    /lenIV 0 def
  37.  
  38. % Invert the StandardEncoding vector.
  39.    256 dict dup begin
  40.    0 1 255 { dup StandardEncoding exch get exch def } for
  41.    end /StandardDecoding exch def
  42.  
  43. % Define the properties copied to FontInfo.
  44.    mark
  45.      (COPYRIGHT) /Notice
  46.      (FAMILY_NAME) /FamilyName
  47.      (FULL_NAME) /FullName
  48.      (WEIGHT_NAME) /Weight
  49.    .dicttomark /properties exch def
  50.  
  51. % Define the character sequences for synthesizing missing composite
  52. % characters in the standard encoding.
  53.    mark
  54.      /AE [/A /E]
  55.      /OE [/O /E]
  56.      /ae [/a /e]
  57.      /ellipsis [/period /period /period]
  58.      /emdash [/hyphen /hyphen /hyphen]
  59.      /endash [/hyphen /hyphen]
  60.      /fi [/f /i]
  61.      /fl [/f /l]
  62.      /germandbls [/s /s]
  63.      /guillemotleft [/less /less]
  64.      /guillemotright [/greater /greater]
  65.      /oe [/o /e]
  66.      /quotedblbase [/comma /comma]
  67.    .dicttomark /composites exch def
  68.  
  69. % Define the procedure for synthesizing composites.
  70. % This must not be bound.
  71.    /compose
  72.     { exch pop
  73.       FontMatrix Private /composematrix get invertmatrix concat
  74.       0 0 moveto
  75.       dup gsave false charpath pathbbox currentpoint grestore
  76.       6 2 roll setcachedevice show
  77.     } def
  78. % Define the CharString procedure that calls compose, with the string
  79. % on the stack.  This too must remain unbound.
  80.    /compose_proc
  81.     { Private /compose get exec
  82.     } def
  83.  
  84. % Define aliases for missing characters similarly.
  85.    mark
  86.      /acute /quoteright
  87.      /bullet /asterisk
  88.      /cedilla /comma
  89.      /circumflex /asciicircum
  90.      /dieresis /quotedbl
  91.      /dotlessi /i
  92.      /exclamdown /exclam
  93.      /florin /f
  94.      /fraction /slash
  95.      /grave /quoteleft
  96.      /guilsinglleft /less
  97.      /guilsinglright /greater
  98.      /hungarumlaut /quotedbl
  99.      /periodcentered /asterisk
  100.      /questiondown /question
  101.      /quotedblleft /quotedbl
  102.      /quotedblright /quotedbl
  103.      /quotesinglbase /comma
  104.      /quotesingle /quoteright
  105.      /tilde /asciitilde
  106.    .dicttomark /aliases exch def
  107.  
  108. % Define overstruck characters that can be synthesized with seac.
  109.    mark
  110.     [ /Aacute /Acircumflex /Adieresis /Agrave /Aring /Atilde
  111.       /Ccedilla
  112.       /Eacute /Ecircumflex /Edieresis /Egrave
  113.       /Iacute /Icircumflex /Idieresis /Igrave
  114.       /Lslash
  115.       /Ntilde
  116.       /Oacute /Ocircumflex /Odieresis /Ograve /Otilde
  117.       /Scaron
  118.       /Uacute /Ucircumflex /Udieresis /Ugrave
  119.       /Yacute /Ydieresis
  120.       /Zcaron
  121.       /aacute /acircumflex /adieresis /agrave /aring /atilde
  122.       /ccedilla
  123.       /eacute /ecircumflex /edieresis /egrave
  124.       /iacute /icircumflex /idieresis /igrave
  125.       /lslash
  126.       /ntilde
  127.       /oacute /ocircumflex /odieresis /ograve /otilde
  128.       /scaron
  129.       /uacute /ucircumflex /udieresis /ugrave
  130.       /yacute /ydieresis
  131.       /zcaron
  132.     ]
  133.     { dup =string cvs
  134.       [ exch dup 0 1 getinterval cvn
  135.     exch dup length 1 sub 1 exch getinterval cvn
  136.       ]
  137.     } forall
  138.      /cent [/c /slash]
  139.      /daggerdbl [/bar /equal]
  140.      /divide [/colon /hyphen]
  141.      /sterling [/L /hyphen]
  142.      /yen [/Y /equal]
  143.    .dicttomark /accentedchars exch def
  144.  
  145. % ------ Output utilities ------ %
  146.  
  147.    /ws {psfile exch writestring} bind def
  148.    /wl {ws (\n) ws} bind def
  149.    /wt {=string cvs ws ( ) ws} bind def
  150.  
  151. % ------ BDF file parsing utilities ------ %
  152.  
  153. % Define a buffer for reading the BDF file.
  154.    /buffer 400 string def
  155.  
  156. % Read a line from the BDF file into the buffer.
  157. % Define /keyword as the first word on the line.
  158. % Define /args as the remainder of the line.
  159. % If the keyword is equal to commentword, skip the line.
  160. % (If commentword is equal to a space, never skip.)
  161.    /nextline
  162.     { bdfile buffer readline not
  163.        { (Premature EOF\n) print stop } if
  164.       ( ) search
  165.        { /keyword exch def pop }
  166.        { /keyword exch def () }
  167.       ifelse
  168.       /args exch def
  169.       keyword commentword eq { nextline } if
  170.     } bind def
  171.  
  172. % Get a word argument from args.  We do *not* copy the string.
  173.    /warg        % warg -> string
  174.     { args ( ) search
  175.        { exch pop exch }
  176.        { () }
  177.       ifelse  /args exch def
  178.     } bind def
  179.  
  180. % Get an integer argument from args.
  181.    /iarg        % iarg -> int
  182.     { warg cvi
  183.     } bind def
  184.  
  185. % Get a numeric argument from args.
  186.    /narg        % narg -> int|real
  187.     { warg cvr
  188.       dup dup cvi eq { cvi } if
  189.     } bind def
  190.  
  191. % Convert the remainder of args into a string.
  192.    /remarg        % remarg -> string
  193.     { args copystring
  194.     } bind def
  195.  
  196. % Get a string argument that occupies the remainder of args.
  197.    /sarg        % sarg -> string
  198.     { args (") anchorsearch
  199.        { pop /args exch def } { pop } ifelse
  200.       args args length 1 sub get (") 0 get eq
  201.        { args 0 args length 1 sub getinterval /args exch def } if
  202.       args copystring
  203.     } bind def
  204.  
  205. % Check that the keyword is the expected one.
  206.    /checkline        % (EXPECTED-KEYWORD) checkline ->
  207.     { dup keyword ne
  208.        { (Expected ) print =
  209.          (Line=) print keyword print ( ) print args print (\n) print stop
  210.        } if
  211.       pop
  212.     } bind def
  213.  
  214. % Read a line and check its keyword.
  215.    /getline        % (EXPECTED-KEYWORD) getline ->
  216.     { nextline checkline
  217.     } bind def
  218.  
  219. % Find the first/last non-zero bit of a non-zero byte.
  220.    /fnzb
  221.     { 0 { exch dup 128 ge { pop exit } { dup add exch 1 add } ifelse }
  222.       loop
  223.     } bind def
  224.    /lnzb
  225.     { 7 { exch dup 1 and 0 ne { pop exit } { -1 bitshift exch 1 sub } ifelse }
  226.       loop
  227.     } bind def
  228.  
  229. % ------ Type 1 encoding utilities ------ %
  230.  
  231. % Parse the side bearing and width information that begins a CharString.
  232. % Arguments: charstring.  Result: sbx sby wx wy substring.
  233.    /parsesbw
  234.     { mark exch lenIV
  235.        {        % stack: mark ... string dropcount
  236.          dup 2 index length exch sub getinterval
  237.      dup 0 get dup 32 lt { pop exit } if
  238.      dup 246 le
  239.       { 139 sub exch 1 }
  240.       { dup 250 le
  241.          { 247 sub 8 bitshift 108 add 1 index 1 get add exch 2 }
  242.          { dup 254 le
  243.         { 251 sub 8 bitshift 108 add 1 index 1 get add neg exch 2 }
  244.         { pop dup 1 get 128 xor 128 sub
  245.           8 bitshift 1 index 2 get add
  246.           8 bitshift 1 index 3 get add
  247.           8 bitshift 1 index 4 get add exch 5
  248.         } ifelse
  249.          } ifelse
  250.       } ifelse
  251.        } loop
  252.       counttomark 3 eq { 0 3 1 roll 0 exch } if
  253.       6 -1 roll pop
  254.     } bind def 
  255.  
  256. % Find the side bearing and width information that begins a CharString.
  257. % Arguments: charstring.  Result: charstring sizethroughsbw.
  258.    /findsbw
  259.     { dup parsesbw 4 { exch pop } repeat skipsbw
  260.     } bind def
  261.    /skipsbw        % charstring sbwprefix -> sizethroughsbw
  262.     { length 1 index length exch sub
  263.       2 copy get 12 eq { 2 } { 1 } ifelse add
  264.     } bind def
  265.  
  266. % Encode a number, and append it to a string.
  267. % Arguments: str num.  Result: newstr.
  268.    /concatnum
  269.     { dup dup -107 ge exch 107 le and
  270.        { 139 add 1 string dup 0 3 index put }
  271.        { dup dup -1131 ge exch 1131 le and
  272.           { dup 0 ge { 16#f694 } { neg 16#fa94 } ifelse add
  273.         2 string dup 0 3 index -8 bitshift put
  274.         dup 1 3 index 255 and put
  275.       }
  276.       { 5 string dup 0 255 put exch
  277.         2 copy 1 exch -24 bitshift 255 and put
  278.         2 copy 2 exch -16 bitshift 255 and put
  279.         2 copy 3 exch -8 bitshift 255 and put
  280.         2 copy 4 exch 255 and put
  281.         exch
  282.       }
  283.      ifelse
  284.        }
  285.       ifelse exch pop concatstrings
  286.     } bind def
  287.  
  288. % ------ Point arithmetic utilities ------ %
  289.  
  290.    /ptadd { exch 4 -1 roll add 3 1 roll add } bind def
  291.    /ptexch { 4 2 roll } bind def
  292.    /ptneg { neg exch neg exch } bind def
  293.    /ptpop { pop pop } bind def
  294.    /ptsub { ptneg ptadd } bind def
  295.  
  296. % ------ The main program ------ %
  297.  
  298.    /readBDF        % <infilename> <outfilename> <fontname>
  299.             %   <encodingname> <uniqueID> <xuid> readBDF -> <font>
  300.     { /xuid exch def        % may be null
  301.       /uniqueID exch def    % may be -1
  302.       /encodingname exch def
  303.     /encoding encodingname cvx exec def
  304.       /fontname exch def
  305.       /psname exch def
  306.       /bdfname exch def
  307.       gsave        % so we can set the CTM to the font matrix
  308.  
  309. %  Open the input files.  We don't open the output file until
  310. %  we've done a minimal validity check on the input.
  311.       bdfname (r) file /bdfile exch def
  312.       /commentword ( ) def
  313.  
  314. %  Check for the STARTFONT.
  315.       (STARTFONT) getline
  316.       args (2.1) ne { (Not version 2.1\n) print stop } if
  317.  
  318. %  Initialize the font.
  319.       /Font 20 dict def
  320.       Font begin
  321.       /FontName fontname def
  322.       /PaintType 0 def
  323.       /FontType 1 def
  324.       uniqueID 0 gt { /UniqueID uniqueID def } if
  325.       xuid null ne { /XUID xuid def } if
  326.       /Encoding encoding def
  327.       /FontInfo 20 dict def
  328.       /Private 20 dict def
  329.       currentdict end currentdict end
  330.       exch begin begin        % insert font above environment
  331.  
  332. %  Initialize the Private dictionary in the font.
  333.       Private begin
  334.       /-! {string currentfile exch readhexstring pop} readonly def
  335.       /-| {string currentfile exch readstring pop} readonly def
  336.       /|- {readonly def} readonly def
  337.       /| {readonly put} readonly def
  338.       /BlueValues [] def
  339.       /lenIV lenIV def
  340.       /MinFeature {16 16} def
  341.       /password 5839 def
  342.       /UniqueID uniqueID def
  343.       end        % Private
  344.  
  345. %  Invert the Encoding, for synthesizing composite characters.
  346.       /decoding encoding length dict def
  347.       0 1 encoding length 1 sub
  348.        { dup encoding exch get exch decoding 3 1 roll put }
  349.       for
  350.  
  351. %  Now open the output file.
  352.       psname (w) file /psfile exch def
  353.  
  354. %  Put out a header compatible with the Adobe "standard".
  355.       (%!FontType1-1.0: ) ws fontname wt (000.000) wl
  356.       (% This is a font description converted from ) ws
  357.     bdfname wl
  358.       (%   by bdftops running on ) ws
  359.       statusdict /product get ws ( revision ) ws
  360.       revision =string cvs ws (.) wl
  361.  
  362. %  Copy the initial comments, up to FONT.
  363.       true
  364.        { nextline
  365.      keyword (COMMENT) ne {exit} if
  366.       { (% Here are the initial comments from the BDF file:\n%) wl
  367.       } if false
  368.      (%) ws remarg wl
  369.        } loop pop
  370.       () wl
  371.       /commentword (COMMENT) def    % do skip comments from now on
  372.  
  373. %  Read and process the FONT, SIZE, and FONTBOUNDINGBOX.
  374.       % If we cared about FONT, we'd use it here.  If the BDF files
  375.       % from MIT had PostScript names rather than X names, we would
  376.       % care; but what's there is unusable, so we discard FONT.
  377.       % The FONTBOUNDINGBOX may not be reliable, so we discard it too.
  378.       (FONT) checkline
  379.       (SIZE) getline
  380.     /pointsize iarg def   /xres iarg def   /yres iarg def
  381.       (FONTBOUNDINGBOX) getline
  382.       nextline
  383.  
  384. %  Initialize the font bounding box bookeeping.
  385.       /fbbxo 1000 def
  386.       /fbbyo 1000 def
  387.       /fbbxe -1000 def
  388.       /fbbye -1000 def
  389.  
  390. %  Read and process the properties.  We only care about a few of them.
  391.       keyword (STARTPROPERTIES) eq
  392.        { iarg
  393.           { nextline
  394.         properties keyword known
  395.          { FontInfo properties keyword get sarg readonly put
  396.          } if
  397.       } repeat
  398.          (ENDPROPERTIES) getline
  399.      nextline
  400.        } if
  401.  
  402. %  Compute and set the FontMatrix.
  403.       Font /FontMatrix
  404.        [ 0.001 0 0 0.001 xres mul yres div 0 0 ] readonly
  405.       dup setmatrix put
  406.  
  407. %  Read and process the header for the bitmaps.
  408.       (CHARS) checkline
  409.     /ccount iarg def
  410.  
  411. %  Initialize the CharStrings dictionary.
  412.       /charstrings ccount
  413.     composites length add
  414.     aliases length add
  415.     accentedchars length add
  416.     1 add dict def        % 1 add for .notdef
  417.       /isfixedwidth true def
  418.       /fixedwidth null def
  419.       /subrcount 0 def
  420.       /subrs [] def
  421.  
  422. %  Read the bitmap data.  This reads the remainder of the file.
  423. %  We do this before processing the bitmaps so that we can compute
  424. %  the correct FontBBox first.
  425.       /chardata ccount dict def
  426.       ccount -1 1
  427.        { (STARTCHAR) getline
  428.            /charname remarg def
  429.      (/) print charname print
  430.        10 mod 1 eq { (\n) print flush } if
  431.      (ENCODING) getline        % Ignore, assume StandardEncoding
  432.      (SWIDTH) getline
  433.        /swx iarg pointsize mul 1000 div xres mul 72 div def
  434.        /swy iarg pointsize mul 1000 div xres mul 72 div def
  435.      (DWIDTH) getline        % Ignore, use SWIDTH instead
  436.      (BBX) getline
  437.        /bbw iarg def  /bbh iarg def  /bbox iarg def  /bboy iarg def
  438.      nextline
  439.      keyword (ATTRIBUTES) eq
  440.       { nextline
  441.       } if
  442.      (BITMAP) checkline
  443.  
  444. % Update the font bounding box.
  445.      /fbbxo fbbxo bbox min def
  446.      /fbbyo fbbyo bboy min def
  447.      /fbbxe fbbxe bbox bbw add max def
  448.      /fbbye fbbye bboy bbh add max def
  449.  
  450. % Read the bits for this character.
  451.      /raster bbw 7 add 8 idiv def
  452.      /cbits raster bbh mul string def
  453.      0 raster cbits length raster sub
  454.       { cbits exch raster getinterval
  455.         bdfile buffer readline not
  456.          { (EOF in bitmap\n) print stop } if
  457.         % stack has <cbits.interval> <buffer.interval>
  458.         0 () /SubFileDecode filter
  459.         exch 2 copy readhexstring pop pop pop closefile
  460.       } for
  461.      (ENDCHAR) getline
  462.  
  463. % Save the character data.
  464.      chardata charname [swx swy bbw bbh bbox bboy cbits] put
  465.        } for
  466.  
  467.       (ENDFONT) getline
  468.  
  469. % Allocate the buffers for the bitmap and the outline,
  470. % according to the font bounding box.
  471.       /fbbw fbbxe fbbxo sub def
  472.       /fbbh fbbye fbbyo sub def
  473.       /fraster fbbw 7 add 8 idiv def
  474.       /bits fraster fbbh mul 200 max 65535 min string def
  475.       /outline bits length 6 mul 65535 min string def
  476.  
  477. %  Process the characters.
  478.       chardata
  479.        { exch /charname exch def  aload pop
  480.      /cbits exch def
  481.      /bboy exch def   /bbox exch def
  482.      /bbh exch def   /bbw exch def
  483.      /swy exch def   /swx exch def
  484.  
  485. % The bitmap handed to type1imagepath must have the correct height,
  486. % because type1imagepath uses this to compute the scale factor,
  487. % so we have to clear the unused parts of it.
  488.      /raster bbw 7 add 8 idiv def
  489.      bits dup 0 1 raster fbbh mul 1 sub
  490.       { 0 put dup } for
  491.      pop pop
  492.      bits raster fbbh bbh sub mul cbits putinterval
  493.  
  494. %  Compute the font entry, converting the bitmap to an outline.
  495.      bits 0 raster fbbh mul getinterval    % the bitmap image
  496.      bbw   fbbh                % bitmap width & height
  497.      swx   swy                % width x & y
  498.      bbox neg   bboy neg            % origin x & y
  499.          % Account for lenIV when converting the outline.
  500.      outline  lenIV  outline length lenIV sub  getinterval
  501.      type1imagepath
  502.      length lenIV add
  503.      outline exch 0 exch getinterval
  504.  
  505. % Check for a fixed width font.
  506.      isfixedwidth
  507.       { fixedwidth null eq
  508.          { /fixedwidth swx def }
  509.          { fixedwidth swx ne { /isfixedwidth false def } if }
  510.         ifelse
  511.       } if
  512.  
  513. % Finish up the character.
  514.      copystring
  515.      charname exch charstrings 3 1 roll put
  516.        } forall
  517.  
  518. %  Add CharStrings entries for aliases.
  519.       aliases
  520.        { charstrings 2 index known not charstrings 2 index known and
  521.           { charstrings exch get charstrings 3 1 roll put
  522.       }
  523.       { pop pop
  524.       }
  525.      ifelse
  526.        }
  527.       forall
  528.  
  529. %  If this is not a fixed-width font, synthesize missing characters
  530. %  out of available ones.
  531.       isfixedwidth not
  532.        { false composites
  533.       { 1 index charstrings exch known not
  534.         1 index { decoding exch known and } forall
  535.          { ( /) print 1 index bits cvs print
  536.            /combine exch def
  537.            0 1 combine length 1 sub
  538.         { dup combine exch get decoding exch get
  539.           bits 3 1 roll put
  540.         } for
  541.            bits 0 combine length getinterval copystring
  542.            [ exch /compose_proc load aload pop ] cvx
  543.            charstrings 3 1 roll put
  544.            pop true
  545.          }
  546.          { pop pop }
  547.         ifelse
  548.       }
  549.      forall flush
  550.       { Private /composematrix matrix put
  551.         Private /compose /compose load put
  552.       }
  553.      if
  554.        }
  555.       if
  556.  
  557. %  Synthesize accented characters with seac if needed and possible.
  558.       accentedchars
  559.        { aload pop /accent exch def /base exch def
  560.          buffer cvs /accented exch def
  561.      charstrings accented known not
  562.      charstrings base known and
  563.      charstrings accent known and
  564.      StandardDecoding base known and
  565.      StandardDecoding accent known and
  566.      encoding StandardDecoding base get get base eq and
  567.      encoding StandardDecoding accent get get accent eq and
  568.       { ( /) print accented print
  569.         charstrings base get findsbw 0 exch getinterval
  570.         /acstring exch def        % start with sbw of base
  571.         charstrings accent get parsesbw
  572.         4 { pop } repeat        % just leave sbx
  573.         acstring exch concatnum
  574.         0 concatnum 0 concatnum        % adx ady
  575.         decoding base get concatnum        % bchar
  576.         decoding accent get concatnum    % achar
  577.         s_seac concatstrings
  578.         charstrings exch accented copystring exch put
  579.       } if
  580.        } forall
  581.  
  582. %  Make a CharStrings entry for .notdef.
  583.       outline lenIV <8b8b0d0e> putinterval    % 0 0 hsbw endchar
  584.       charstrings /.notdef outline 0 lenIV 4 add getinterval copystring put
  585.  
  586. %  Encrypt the CharStrings and Subrs (in place).
  587.       charstrings
  588.        {    % Be careful not to encrypt aliased characters twice,
  589.         % since they share their CharString.
  590.      aliases 2 index known
  591.       { charstrings aliases 3 index get .knownget
  592.          { 1 index ne }
  593.          { true }
  594.         ifelse
  595.       }
  596.       { true
  597.       }
  598.      ifelse
  599.      1 index type /stringtype eq and
  600.           { 4330 exch dup .type1encrypt exch pop
  601.         readonly charstrings 3 1 roll put
  602.       }
  603.       { pop pop
  604.       }
  605.      ifelse
  606.        }
  607.       forall
  608.       0 1 subrcount 1 sub
  609.        { dup subrs exch get
  610.      4330 exch dup .type1encrypt exch pop
  611.      subrs 3 1 roll put
  612.        }
  613.       for
  614.  
  615. %  Make most of the remaining entries in the font dictionaries.
  616.  
  617. % The Type 1 font machinery really only works with a 1000 unit
  618. % character coordinate system.  Set this up here, by computing the factor
  619. % to make the X entry in the FontMatrix come out at exactly 0.001.
  620.       /fontscale 1000 fbbh div yres mul xres div def
  621.       Font /FontBBox
  622.        [ fbbxo fontscale mul
  623.      fbbyo fontscale mul
  624.      fbbxe fontscale mul
  625.      fbbye fontscale mul
  626.        ] cvx readonly put
  627.       Font /CharStrings charstrings readonly put
  628.       FontInfo /FullName known not
  629.        { % Some programs insist on FullName being present.
  630.          FontInfo /FullName FontName dup length string cvs put
  631.        }
  632.       if
  633.       FontInfo /isFixedPitch isfixedwidth put
  634.       subrcount 0 gt
  635.        { Private /Subrs subrs 0 subrcount getinterval readonly put
  636.        } if
  637.  
  638. %  Determine the italic angle and underline position
  639. %  by actually installing the font.
  640.       save
  641.       /_temp_ Font definefont setfont
  642.       [1000 0 0 1000 0 0] setmatrix        % mitigate rounding problems
  643. % The italic angle is the multiple of -5 degrees
  644. % that minimizes the width of the 'I'.
  645.       0 9999 0 5 85
  646.        { dup rotate
  647.          newpath 0 0 moveto (I) false charpath
  648.      dup neg rotate
  649.          pathbbox pop exch pop exch sub
  650.      dup 3 index lt { 4 -2 roll } if
  651.      pop pop
  652.        }
  653.       for pop
  654. % The underline position is halfway between the bottom of the 'A'
  655. % and the bottom of the FontBBox.
  656.       newpath 0 0 moveto (A) false charpath
  657.       FontMatrix concat
  658.       pathbbox pop pop exch pop
  659. %  Put the values in FontInfo.
  660.       3 -1 roll
  661.       restore
  662.       Font /FontBBox get 1 get add 2 div cvi
  663.       dup FontInfo /UnderlinePosition 3 -1 roll put
  664.       2 div abs FontInfo /UnderlineThickness 3 -1 roll put
  665.       FontInfo /ItalicAngle 3 -1 roll put
  666.  
  667. %  Clean up and finish.
  668.       grestore
  669.       bdfile closefile
  670.       Font currentdict end end begin        % remove font from dict stack
  671.       (\n) print flush
  672.  
  673.     } bind def
  674.  
  675. % ------ Reader for AFM files ------ %
  676.  
  677. % Dictionary for looking up character keywords
  678.    /cmdict 6 dict dup begin
  679.       /C { /c iarg def } def
  680.       /N { /n warg copystring def } def
  681.       /WX { /w narg def } def
  682.       /W0X /WX load def
  683.       /W /WX load def
  684.       /W0 /WX load def
  685.    end def
  686.  
  687.    /readAFM        % fontdict afmfilename readAFM -> fontdict
  688.     { (r) file /bdfile exch def
  689.       /Font exch def
  690.       /commentword (Comment) def
  691.  
  692. %  Check for the StartFontMetrics.
  693.       (StartFontMetrics) getline
  694.       args cvr 2.0 lt { (Not version 2.0 or greater\n) print stop } if
  695.  
  696. %  Look for StartCharMetrics, then parse the character metrics.
  697. %  The only information we care about is the X width.
  698.       /metrics 0 dict def
  699.        { nextline
  700.          keyword (EndFontMetrics) eq { exit } if
  701.      keyword (StartCharMetrics) eq
  702.       { iarg dup dict /metrics exch def
  703.          { /c -1 def /n null def /w null def
  704.            nextline buffer
  705.         { token not { exit } if
  706.           dup cmdict exch known
  707.            { exch /args exch def   cmdict exch get exec   args }
  708.            { pop }
  709.           ifelse
  710.         } loop
  711.            c 0 ge n null ne or w null ne and
  712.         { n null eq { /n Font /Encoding get c get def } if
  713.           metrics n w put
  714.         }
  715.            if
  716.          }
  717.         repeat
  718.         (EndCharMetrics) getline
  719.       } if
  720.        } loop
  721.  
  722. %  Insert the metrics in the font.
  723.        metrics length 0 ne
  724.     { Font /Metrics metrics readonly put
  725.     } if
  726.       Font
  727.     } bind def
  728.  
  729. end        % envBDF
  730.  
  731. % Enter the main program in the current dictionary.
  732. /bdfafmtops        % infilename afmfilename* outfilename fontname
  733.             %   encodingname uniqueID xuid
  734.  { envBDF begin
  735.      7 -2 roll exch 7 2 roll    % afm* in out fontname encodingname uniqueID xuid
  736.      readBDF        % afm* font
  737.      exch { readAFM } forall
  738.      save exch
  739.      dup /FontName get exch definefont
  740.      setfont
  741.      psfile writefont
  742.      restore
  743.      psfile closefile
  744.    end
  745.  } bind def
  746.  
  747. % If the program was invoked from the command line, run it now.
  748. [ shellarguments
  749.  { counttomark 4 ge
  750.     { dup 0 get
  751.       dup 48 ge exch 57 le and        % last arg starts with a digit?
  752.        { /StandardEncoding }        % no encodingname
  753.        { cvn }                % have encodingname
  754.       ifelse
  755.       exch (.) search            % next-to-last arg has . in it?
  756.        { mark 4 1 roll            % have xuid
  757.           { cvi exch pop exch (.) search not { exit } if }
  758.      loop cvi ]
  759.      3 -1 roll cvi exch
  760.        }
  761.        { cvi null            % no xuid
  762.        }
  763.       ifelse
  764.       counttomark 5 roll
  765.       counttomark 6 sub array astore
  766.       7 -2 roll cvn 7 -3 roll        % make sure fontname is a name
  767.       bdfafmtops
  768.     }
  769.     { cleartomark
  770.       (Usage:\n  bdftops xx.bdf [yy1.afm ...] zz.gsf fontname uniqueID [xuid] [encodingname]\n) print flush
  771.       mark
  772.     }
  773.    ifelse
  774.  }
  775. if pop
  776.