1 % Copyright (C) 1990, 1995, 1996 Aladdin Enterprises. All rights reserved.
3 % This software is provided AS-IS with no warranty, either express or
6 % This software is distributed under license and may not be copied,
7 % modified or distributed except as expressly authorized under the terms
8 % of the license contained in the file LICENSE in this distribution.
10 % For more information about licensing, please refer to
11 % http://www.ghostscript.com/licensing/. For information on
12 % commercial licensing, go to http://www.artifex.com/licensing/ or
13 % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 % San Rafael, CA 94903, U.S.A., +1(415)492-9861.
16 % $Id: bdftops.ps,v 1.7 2003/08/06 17:05:09 alexcher Exp $
18 % Convert a BDF file (possibly with (an) associated AFM file(s))
19 % to a PostScript Type 1 font (without eexec encryption).
20 % The resulting font will work with any PostScript language interpreter,
21 % but not with ATM or other font rasterizers lacking a complete interpreter.
26 % "Import" the image-to-path package.
27 % This also brings in the Type 1 opcodes (type1ops.ps).
28 (impath.ps) runlibfile
30 % "Import" the font-writing package.
31 (wrfont.ps) runlibfile
33 /binary_CharStrings false def
34 /binary_tokens false def
35 /encrypt_CharStrings true def
36 /standard_only true def
40 % Invert the StandardEncoding vector.
42 0 1 255 { dup StandardEncoding exch get exch def } for
43 end /StandardDecoding exch def
45 % Define the properties copied to FontInfo.
48 (FAMILY_NAME) /FamilyName
51 .dicttomark /properties exch def
53 % Define the character sequences for synthesizing missing composite
54 % characters in the standard encoding.
59 /ellipsis [/period /period /period]
60 /emdash [/hyphen /hyphen /hyphen]
61 /endash [/hyphen /hyphen]
65 /guillemotleft [/less /less]
66 /guillemotright [/greater /greater]
68 /quotedblbase [/comma /comma]
69 .dicttomark /composites exch def
71 % Define the procedure for synthesizing composites.
72 % This must not be bound.
75 FontMatrix Private /composematrix get invertmatrix concat
77 dup gsave false charpath pathbbox currentpoint grestore
78 6 2 roll setcachedevice show
80 % Define the CharString procedure that calls compose, with the string
81 % on the stack. This too must remain unbound.
83 { Private /compose get exec
86 % Define aliases for missing characters similarly.
91 /circumflex /asciicircum
99 /guilsinglright /greater
100 /hungarumlaut /quotedbl
101 /periodcentered /asterisk
102 /questiondown /question
103 /quotedblleft /quotedbl
104 /quotedblright /quotedbl
105 /quotesinglbase /comma
106 /quotesingle /quoteright
108 .dicttomark /aliases exch def
110 % Define overstruck characters that can be synthesized with seac.
112 [ /Aacute /Acircumflex /Adieresis /Agrave /Aring /Atilde
114 /Eacute /Ecircumflex /Edieresis /Egrave
115 /Iacute /Icircumflex /Idieresis /Igrave
118 /Oacute /Ocircumflex /Odieresis /Ograve /Otilde
120 /Uacute /Ucircumflex /Udieresis /Ugrave
123 /aacute /acircumflex /adieresis /agrave /aring /atilde
125 /eacute /ecircumflex /edieresis /egrave
126 /iacute /icircumflex /idieresis /igrave
129 /oacute /ocircumflex /odieresis /ograve /otilde
131 /uacute /ucircumflex /udieresis /ugrave
136 [ exch dup 0 1 getinterval cvn
137 exch dup length 1 sub 1 exch getinterval cvn
141 /daggerdbl [/bar /equal]
142 /divide [/colon /hyphen]
143 /sterling [/L /hyphen]
145 .dicttomark /accentedchars exch def
147 % ------ Output utilities ------ %
149 /ws {psfile exch writestring} bind def
150 /wl {ws (\n) ws} bind def
151 /wt {=string cvs ws ( ) ws} bind def
153 % ------ BDF file parsing utilities ------ %
155 % Define a buffer for reading the BDF file.
156 /buffer 400 string def
158 % Read a line from the BDF file into the buffer.
159 % Ignore empty (zero-length) lines.
160 % Define /keyword as the first word on the line.
161 % Define /args as the remainder of the line.
162 % If the keyword is equal to commentword, skip the line.
163 % (If commentword is equal to a space, never skip.)
165 { { bdfile buffer readline not
166 { (Premature EOF\n) print stop } if
167 dup length 0 ne { exit } if pop
171 { /keyword exch def pop }
172 { /keyword exch def () }
175 keyword commentword eq { nextline } if
178 % Get a word argument from args. We do *not* copy the string.
179 /warg % warg -> string
183 ifelse /args exch def
186 % Get an integer argument from args.
191 % Get a numeric argument from args.
192 /narg % narg -> int|real
194 dup dup cvi eq { cvi } if
197 % Convert the remainder of args into a string.
198 /remarg % remarg -> string
202 % Get a string argument that occupies the remainder of args.
203 /sarg % sarg -> string
204 { args (") anchorsearch
205 { pop /args exch def } { pop } ifelse
206 args args length 1 sub get (") 0 get eq
207 { args 0 args length 1 sub getinterval /args exch def } if
211 % Check that the keyword is the expected one.
212 /checkline % (EXPECTED-KEYWORD) checkline ->
214 { (Expected ) print =
215 (Line=) print keyword print ( ) print args print (\n) print stop
220 % Read a line and check its keyword.
221 /getline % (EXPECTED-KEYWORD) getline ->
225 % Find the first/last non-zero bit of a non-zero byte.
227 { 0 { exch dup 128 ge { pop exit } { dup add exch 1 add } ifelse }
231 { 7 { exch dup 1 and 0 ne { pop exit } { -1 bitshift exch 1 sub } ifelse }
235 % ------ Type 1 encoding utilities ------ %
237 % Parse the side bearing and width information that begins a CharString.
238 % Arguments: charstring. Result: sbx sby wx wy substring.
241 { % stack: mark ... string dropcount
242 dup 2 index length exch sub getinterval
243 dup 0 get dup 32 lt { pop exit } if
247 { 247 sub 8 bitshift 108 add 1 index 1 get add exch 2 }
249 { 251 sub 8 bitshift 108 add 1 index 1 get add neg exch 2 }
250 { pop dup 1 get 128 xor 128 sub
251 8 bitshift 1 index 2 get add
252 8 bitshift 1 index 3 get add
253 8 bitshift 1 index 4 get add exch 5
258 counttomark 3 eq { 0 3 1 roll 0 exch } if
262 % Find the side bearing and width information that begins a CharString.
263 % Arguments: charstring. Result: charstring sizethroughsbw.
265 { dup parsesbw 4 { exch pop } repeat skipsbw
267 /skipsbw % charstring sbwprefix -> sizethroughsbw
268 { length 1 index length exch sub
269 2 copy get 12 eq { 2 } { 1 } ifelse add
272 % Encode a number, and append it to a string.
273 % Arguments: str num. Result: newstr.
275 { dup dup -107 ge exch 107 le and
276 { 139 add 1 string dup 0 3 index put }
277 { dup dup -1131 ge exch 1131 le and
278 { dup 0 ge { 16#f694 } { neg 16#fa94 } ifelse add
279 2 string dup 0 3 index -8 bitshift put
280 dup 1 3 index 255 and put
282 { 5 string dup 0 255 put exch
283 2 copy 1 exch -24 bitshift 255 and put
284 2 copy 2 exch -16 bitshift 255 and put
285 2 copy 3 exch -8 bitshift 255 and put
286 2 copy 4 exch 255 and put
291 ifelse exch pop concatstrings
294 % ------ Point arithmetic utilities ------ %
296 /ptadd { exch 4 -1 roll add 3 1 roll add } bind def
297 /ptexch { 4 2 roll } bind def
298 /ptneg { neg exch neg exch } bind def
299 /ptpop { pop pop } bind def
300 /ptsub { ptneg ptadd } bind def
302 % ------ The main program ------ %
304 /readBDF % <infilename> <outfilename> <fontname>
305 % <encodingname> <uniqueID> <xuid> readBDF -> <font>
306 { /xuid exch def % may be null
307 /uniqueID exch def % may be -1
308 /encodingname exch def
309 /encoding encodingname cvx exec def
313 gsave % so we can set the CTM to the font matrix
315 % Open the input files. We don't open the output file until
316 % we've done a minimal validity check on the input.
317 bdfname (r) file /bdfile exch def
320 % Check for the STARTFONT.
322 args (2.1) ne { (Not version 2.1\n) print stop } if
324 % Initialize the font.
327 /FontName fontname def
330 uniqueID 0 gt { /UniqueID uniqueID def } if
331 xuid null ne { /XUID xuid def } if
332 /Encoding encoding def
333 /FontInfo 20 dict def
335 currentdict end currentdict end
336 exch begin begin % insert font above environment
338 % Initialize the Private dictionary in the font.
340 /-! {string currentfile exch readhexstring pop} readonly def
341 /-| {string currentfile exch readstring pop} readonly def
342 /|- {readonly def} readonly def
343 /| {readonly put} readonly def
346 /MinFeature {16 16} def
348 /UniqueID uniqueID def
351 % Invert the Encoding, for synthesizing composite characters.
352 /decoding encoding length dict def
353 0 1 encoding length 1 sub
354 { dup encoding exch get exch decoding 3 1 roll put }
357 % Now open the output file.
358 psname (w) file /psfile exch def
360 % Put out a header compatible with the Adobe "standard".
361 (%!FontType1-1.0: ) ws fontname wt (000.000) wl
362 (% This is a font description converted from ) ws
364 (% by bdftops running on ) ws
365 statusdict /product get ws ( revision ) ws
366 revision =string cvs ws (.) wl
368 % Copy the initial comments, up to FONT.
371 keyword (COMMENT) ne {exit} if
372 { (% Here are the initial comments from the BDF file:\n%) wl
377 /commentword (COMMENT) def % do skip comments from now on
379 % Read and process the FONT, SIZE, and FONTBOUNDINGBOX.
380 % If we cared about FONT, we'd use it here. If the BDF files
381 % from MIT had PostScript names rather than X names, we would
382 % care; but what's there is unusable, so we discard FONT.
383 % The FONTBOUNDINGBOX may not be reliable, so we discard it too.
386 /pointsize iarg def /xres iarg def /yres iarg def
387 (FONTBOUNDINGBOX) getline
390 % Initialize the font bounding box bookeeping.
396 % Read and process the properties. We only care about a few of them.
397 keyword (STARTPROPERTIES) eq
400 properties keyword known
401 { FontInfo properties keyword get sarg readonly put
404 (ENDPROPERTIES) getline
408 % Compute and set the FontMatrix.
410 [ 0.001 0 0 0.001 xres mul yres div 0 0 ] readonly
413 % Read and process the header for the bitmaps.
417 % Initialize the CharStrings dictionary.
419 composites length add
421 accentedchars length add
422 1 add dict def % 1 add for .notdef
423 /isfixedwidth true def
428 % Read the bitmap data. This reads the remainder of the file.
429 % We do this before processing the bitmaps so that we can compute
430 % the correct FontBBox first.
431 /chardata ccount dict def
433 { (STARTCHAR) getline
437 eindex dup 0 ge exch 255 le and
438 { charname /charname StandardEncoding eindex get def
439 charname /.notdef eq eindex 0 gt and
440 { /charname (A) eindex =string cvs concatstrings cvn def
443 (/) print charname =string cvs print (,) print print
445 { (/) print charname print
448 10 mod 1 eq { (\n) print flush } if
450 /swx iarg pointsize mul 1000 div xres mul 72 div def
451 /swy iarg pointsize mul 1000 div xres mul 72 div def
452 (DWIDTH) getline % Ignore, use SWIDTH instead
454 /bbw iarg def /bbh iarg def /bbox iarg def /bboy iarg def
456 keyword (ATTRIBUTES) eq
461 % Update the font bounding box.
462 /fbbxo fbbxo bbox .min def
463 /fbbyo fbbyo bboy .min def
464 /fbbxe fbbxe bbox bbw add .max def
465 /fbbye fbbye bboy bbh add .max def
467 % Read the bits for this character.
468 /raster bbw 7 add 8 idiv def
469 /cbits raster bbh mul string def
471 { 0 raster cbits length raster sub
472 { cbits exch raster getinterval
473 bdfile buffer readline not
474 { (EOF in bitmap\n) print stop } if
475 % stack has <cbits.interval> <buffer.interval>
476 0 () /SubFileDecode filter
477 exch 2 copy readhexstring pop pop pop closefile
483 % Save the character data.
484 chardata charname [swx swy bbw bbh bbox bboy cbits] put
489 % Allocate the buffers for the bitmap and the outline,
490 % according to the font bounding box.
491 /fbbw fbbxe fbbxo sub def
492 /fbbh fbbye fbbyo sub def
493 /fraster fbbw 7 add 8 idiv def
494 /bits fraster fbbh mul 200 .max 65535 .min string def
495 /outline bits length 16 mul 65535 .min string def
497 % Process the characters.
499 { exch /charname exch def aload pop
501 /bboy exch def /bbox exch def
502 /bbh exch def /bbw exch def
503 /swy exch def /swx exch def
505 % The bitmap handed to type1imagepath must have the correct height,
506 % because type1imagepath uses this to compute the scale factor,
507 % so we have to clear the unused parts of it.
508 /raster bbw 7 add 8 idiv def
509 bits dup 0 1 raster fbbh mul 1 sub
512 bits raster fbbh bbh sub mul cbits putinterval
514 % Compute the font entry, converting the bitmap to an outline.
515 bits 0 raster fbbh mul getinterval % the bitmap image
516 bbw fbbh % bitmap width & height
517 swx swy % width x & y
518 bbox neg bboy neg % origin x & y
519 % Account for lenIV when converting the outline.
520 outline lenIV outline length lenIV sub getinterval
523 outline exch 0 exch getinterval
525 % Check for a fixed width font.
528 { /fixedwidth swx def }
529 { fixedwidth swx ne { /isfixedwidth false def } if }
533 % Finish up the character.
535 charname exch charstrings 3 1 roll put
538 % Add CharStrings entries for aliases.
540 { charstrings 2 index known not charstrings 2 index known and
541 { charstrings exch get charstrings 3 1 roll put
549 % If this is not a fixed-width font, synthesize missing characters
550 % out of available ones.
553 { 1 index charstrings exch known not
554 1 index { decoding exch known and } forall
555 { ( /) print 1 index bits cvs print
557 0 1 combine length 1 sub
558 { dup combine exch get decoding exch get
561 bits 0 combine length getinterval copystring
562 [ exch /compose_proc load aload pop ] cvx
563 charstrings 3 1 roll put
570 { Private /composematrix matrix put
571 Private /compose /compose load put
577 % Synthesize accented characters with seac if needed and possible.
579 { aload pop /accent exch def /base exch def
580 buffer cvs /accented exch def
581 charstrings accented known not
582 charstrings base known and
583 charstrings accent known and
584 StandardDecoding base known and
585 StandardDecoding accent known and
586 encoding StandardDecoding base get get base eq and
587 encoding StandardDecoding accent get get accent eq and
588 { ( /) print accented print
589 charstrings base get findsbw 0 exch getinterval
590 /acstring exch def % start with sbw of base
591 charstrings accent get parsesbw
592 4 { pop } repeat % just leave sbx
593 acstring exch concatnum
594 0 concatnum 0 concatnum % adx ady
595 decoding base get concatnum % bchar
596 decoding accent get concatnum % achar
598 charstrings exch accented copystring exch put
602 % Make a CharStrings entry for .notdef.
603 outline lenIV <8b8b0d0e> putinterval % 0 0 hsbw endchar
604 charstrings /.notdef outline 0 lenIV 4 add getinterval copystring put
606 % Encrypt the CharStrings and Subrs (in place).
608 { % Be careful not to encrypt aliased characters twice,
609 % since they share their CharString.
610 aliases 2 index known
611 { charstrings aliases 3 index get .knownget
619 1 index type /stringtype eq and
620 { 4330 exch dup .type1encrypt exch pop
621 readonly charstrings 3 1 roll put
630 4330 exch dup .type1encrypt exch pop
635 % Make most of the remaining entries in the font dictionaries.
637 % The Type 1 font machinery really only works with a 1000 unit
638 % character coordinate system. Set this up here, by computing the factor
639 % to make the X entry in the FontMatrix come out at exactly 0.001.
640 /fontscale 1000 fbbh div yres mul xres div def
642 [ fbbxo fontscale mul
647 Font /CharStrings charstrings readonly put
648 FontInfo /FullName known not
649 { % Some programs insist on FullName being present.
650 FontInfo /FullName FontName dup length string cvs put
653 FontInfo /isFixedPitch isfixedwidth put
655 { Private /Subrs subrs 0 subrcount getinterval readonly put
658 % Determine the italic angle and underline position
659 % by actually installing the font.
661 /_temp_ Font definefont setfont
662 [1000 0 0 1000 0 0] setmatrix % mitigate rounding problems
663 % The italic angle is the multiple of -5 degrees
664 % that minimizes the width of the 'I'.
667 newpath 0 0 moveto (I) false charpath
669 pathbbox pop exch pop exch sub
670 dup 3 index lt { 4 -2 roll } if
674 % The underline position is halfway between the bottom of the 'A'
675 % and the bottom of the FontBBox.
676 newpath 0 0 moveto (A) false charpath
678 pathbbox pop pop exch pop
679 % Put the values in FontInfo.
682 Font /FontBBox get 1 get add 2 div cvi
683 dup FontInfo /UnderlinePosition 3 -1 roll put
684 2 div abs FontInfo /UnderlineThickness 3 -1 roll put
685 FontInfo /ItalicAngle 3 -1 roll put
687 % Clean up and finish.
690 Font currentdict end end begin % remove font from dict stack
695 % ------ Reader for AFM files ------ %
697 % Dictionary for looking up character keywords
698 /cmdict 6 dict dup begin
699 /C { /c iarg def } def
700 /N { /n warg copystring def } def
701 /WX { /w narg def } def
707 /readAFM % fontdict afmfilename readAFM -> fontdict
708 { (r) file /bdfile exch def
710 /commentword (Comment) def
712 % Check for the StartFontMetrics.
713 (StartFontMetrics) getline
714 args cvr 2.0 lt { (Not version 2.0 or greater\n) print stop } if
716 % Look for StartCharMetrics, then parse the character metrics.
717 % The only information we care about is the X width.
720 keyword (EndFontMetrics) eq { exit } if
721 keyword (StartCharMetrics) eq
722 { iarg dup dict /metrics exch def
723 { /c -1 def /n null def /w null def
725 { token not { exit } if
726 dup cmdict exch known
727 { exch /args exch def cmdict exch get exec args }
731 c 0 ge n null ne or w null ne and
732 { n null eq { /n Font /Encoding get c get def } if
738 (EndCharMetrics) getline
742 % Insert the metrics in the font.
744 { Font /Metrics metrics readonly put
751 % Enter the main program in the current dictionary.
752 /bdfafmtops % infilename afmfilename* outfilename fontname
753 % encodingname uniqueID xuid
755 7 -2 roll exch 7 2 roll % afm* in out fontname encodingname uniqueID xuid
757 exch { readAFM } forall
759 dup /FontName get exch definefont
767 % If the program was invoked from the command line, run it now.
771 dup 48 ge exch 57 le and % last arg starts with a digit?
772 { /StandardEncoding } % no encodingname
773 { cvn } % have encodingname
775 exch (.) search % next-to-last arg has . in it?
776 { mark 4 1 roll % have xuid
777 { cvi exch pop exch (.) search not { exit } if }
785 counttomark 6 sub array astore
786 7 -2 roll cvn 7 -3 roll % make sure fontname is a name
790 (Usage:\n bdftops xx.bdf [yy1.afm ...] zz.gsf fontname uniqueID [xuid] [encodingname]\n) print flush