// PLMaker.bcpl // edited by Ramshaw December 23, 1980 1:57 PM remove f-kerning // for italic fonts, since italic correction takes care of it // edited by Ramshaw December 14, 1980 5:04 PM new PL and TFM format, // more data about fonts. Added PLMakerData module // edited by Ramshaw July 11, 1980 11:58 AM ignore chars above #177, // plus new families // edited by Guibas October 18, 1979 8:54 AM new OS plus more families // edited by Wyatt September 4, 1979 1:59 PM (NEW PL FORMAT) // edited by Wyatt May 3, 1979 3:02 PM (changed SPACE computation) // edited by Wyatt April 29, 1979 5:43 PM (added TIMESROMANSC,HELVETICASC) // edited by Wyatt February 19, 1979 3:18 PM // edited by Guibas February 18, 1979 1:13 PM to add trident code // modified 2/12/79 to create .PL files for an entire character dictionary // load with: Bldr PLMaker PLMaker1 PLMakerData gp tfs* loadram triconmc // (tfs* from <alto>tfs.dm) // read a prepress character (.ac) file, and produce a property list (.PL) // file suitable for consumption by a sail program which produces // "TFP" files for TEX get "altodefs.d"; // from [maxc]<alto> get "streams.d"; // from [maxc]<alto> get "ix.dfs"; // from [ivy]<tex> get "plmaker.d"; // from [ivy]<tex> manifest [ cr=$*n quadchar=#17 // em space in newer PARC fonts ] external [ // from GP SetupReadParam; ReadParam; // from OS GetFixed; FixedLeft; InitializeZone; Allocate; Free; OpenFile; ReadBlock; WriteBlock; Gets; Puts; Endofs; Resets; Closes; Ws; Wo; Wns; Wss; SetFilePos; FilePos; DoubleAdd; MoveBlock; SetBlock; Usc; Min; Max; fpComCm; dsp; SetEndCode; lvUserFinishProc; lvSwatContextProc; // Trident disk stuff LoadRam; // from LoadRam DiskRamImage; // from TriConMc TFSSwatContextProc; TFSClose; TFSInit; TFSSilentBoot; // from TFS* TFSzone; TFSdisk; // from PLMaker1 Str; Chr; Cr; Dec; UDec; Oct; DNum; ONum; XNum; Char; RNumOver10; Begin; End; SetPos; GetPos; IncPos; DoubleSub; MulDiv; WordsForString; CopyString; StringMatch; AppendChar; AppendNum; AppendStr; DefaultExtension; StripExtension; //from PLMakerData InitTables // outgoing statics outstream; lev; z; flags; tflags; names; tnames; nt; cwvec; // outgoing proc (to PlMakerData) MakeStringCopy ]; static [ z // free storage zone logstream // stream for display and log file infilename outfilename instream1=0 // (word) stream on the input file instream2=0 // another (word) stream on the input file outstream=0 // (byte) stream on the output file bc; ec; // beginning and ending chars nc; // number of characters fam; face; siz; rot; // family code, face code, size in micas, rotation sl; wt; ex; // slope, weight, expansion (e.g., $I, $M, $R) resx; resy; // resolutions (10*number of bits per inch) cwvec // pointer to CharWidth array from input file dirvec // pointer to directory array from input file cwLen // length of cwvec dirLen // length of dirvec dirfp // file position of directory lev=0 // indentation level fixedPitch // true if font has fixed pitch fLigs // true if font has "f" ligatures dLigs // true if font has en-dash and em-dash mQuad // true if font has em-quad s40 // true if #40 is a word space CScode // codingscheme code number pts // size of font in PARC points from the dictionary file texStyle=true // true if PL file is being produced for TEX. names // pointer to array of names from dictionary flags // pointer to array of flags associated with names tnames // pointer to array of family names to be considered tflags // pointer to array of flags associated with tnames nt // number of entries in tnames savedUFP; savedSCP; TFSzone; TFSdisk = 0 // for the Trident ]; structure S: // BCPL string [ length byte body↑0,255 byte ]; let main() be [ OpenLog("PLMaker.log"); Log("PLMaker of December 18, 1980:*n"); InitStorage(); ReadComCm(); Log("Reading file: "); Log(infilename); Log(".*n*n"); OpenInputFile(); InitTables(); ScanDictionary(); CloseFiles(); ]; and InitStorage() be [ let res = LoadRam(DiskRamImage,true); let AltoVersion=(table [ #61014; #1401 ] )() let eng=AltoVersion<<VERS.eng if (res ls 0) & (eng ls 4) then [ Ws("Cannot load the RAM."); finish ]; //don't worry if the RAM won't load on a Dolphin or Dorado savedUFP=@lvUserFinishProc; @lvUserFinishProc=MyFinish; savedSCP=@lvSwatContextProc; @lvSwatContextProc=TFSSwatContextProc; // make a storage zone manifest maxzlength=77777b; // max length for a zone let zlength=FixedLeft()-2500; // maximum available space if Usc(zlength, maxzlength) gr 0 do zlength=maxzlength; z=InitializeZone(GetFixed(zlength), zlength); TFSdisk=TFSInit(z, true, (eng eq 5?1,0)); //drive number is 1 on Dorado ]; and OpenLog(logname) be [ logstream=OpenFile(logname, ksTypeWriteOnly, charItem); if logstream eq 0 do [ Ws("Can't open log file"); abort ]; ]; and Log(s) be [ Ws(s); Wss(logstream,s) ]; // writes string s on both display and log file and ReadComCm() be [ let sv=vec 50; // space for file names let swv=vec 50; // space for switches // prepare to read Com.Cm SetupReadParam(sv, swv); // interpret global switches for i=1 to swv!0 do switchon swv!i into [ case $T: case $t: texStyle=true; endcase; ]; // get input file name from command line ReadParam($P, "Dictionary file: "); infilename=MakeStringCopy(sv); ]; and MyFinish() be [ if TFSdisk ne 0 then TFSClose(TFSdisk) @lvUserFinishProc=savedUFP @lvSwatContextProc=savedSCP TFSSilentBoot() finish ] and OpenInputFile() be [ if TFSdisk then [ instream1=OpenFile(infilename,ksTypeReadOnly,wordItem,0,0,0,z,0,TFSdisk) instream2=OpenFile(infilename,ksTypeReadOnly,wordItem,0,0,0,z,0,TFSdisk) // SetEndCode(TFSInit); ] //now, try to find it on 31 if instream1 eq 0 then [ instream1=OpenFile(infilename, ksTypeReadOnly, wordItem); instream2=OpenFile(infilename, ksTypeReadOnly, wordItem); ] if instream1 eq 0 do Punt("Can't open input file"); ]; and CloseFiles() be [ Closes(logstream); unless instream1 eq 0 do Closes(instream1); unless instream2 eq 0 do Closes(instream2); unless outstream eq 0 do Closes(outstream); ]; and ScanDictionary() be [ // scan through the dictionary index [ let ix=vec IXLMax; // space for index entry ix!0=Gets(instream1); // first word of ix contains type and length if ix>>IX.Type eq IXTypeEnd break; // end of index ReadBlock(instream1, ix+1, ix>>IX.Length-1); // read rest of index entry switchon ix>>IX.Type into [ case IXTypeName: EnterName(lv ix>>IXN.Name, ix>>IXN.Code); endcase; case IXTypeChars: case IXTypeOrbitChars: case IXTypeMultiChars: ProcessChars(ix); default: endcase; // just ignore it ]; ] repeat ]; and EnterName(name, code) be [ names!code=MakeStringCopy(name); // look up the name in tnames for i=0 to nt-1 do [ if StringMatch(name,tnames!i) do [ flags!code=tflags!i; (flags!code)<<incl=1; return; ]; ]; // name not found in tnames (flags!code)<<incl=0; ]; and ProcessChars(ix) be [ // construct font name let psa=nil; // POINTER TO segment starting address switchon ix>>IX.Type into [ case IXTypeChars: case IXTypeOrbitChars: fam=ix>>IX.fam; face=ix>>IX.face; siz=ix>>IX.siz; bc=ix>>IX.bc; ec=ix>>IX.ec; rot=ix>>IX.rotation; resx=ix>>IX.resolutionx; resy=ix>>IX.resolutiony; psa=lv ix>>IX.sa; endcase; case IXTypeMultiChars: fam=ix>>IXM.fam; face=ix>>IXM.face; siz=ix>>IXM.siz; bc=ix>>IXM.bc; ec=ix>>IXM.ec; rot=ix>>IXM.rotation; resx=ix>>IXM.resolutionx; resy=ix>>IXM.resolutiony; psa=lv ix>>IXM.segs↑1.sa; // most recent widths are in 1st seg endcase; default: Log("[??? IX.Type not chars]*n"); return; ]; pts=MulDiv(siz,72,2540); // point size if PARCface(face) then DecodeFace(face, lv wt, lv sl, lv ex); let name=vec 30; // space for the name CopyString(name, names!fam); // first, the family name if (flags!fam)<<ptsize eq 0 then AppendNum(name, pts); // font doesn't //scale, so point size should be in file name //now, put the face into the file name: test PARCface(face) ifso [ if wt ne $M do AppendChar(name, wt); // now the face if sl ne $R do AppendChar(name, sl); if ex ne $R do AppendChar(name, ex); ] ifnot [ AppendNum(name,(254-face)/2); //design size in points if (face&1) ne 0 then AppendChar(name, $H); // and a half ] Log(name); if (flags!fam)<<ptsize ne 0 then [ let s=vec 20 CopyString(s, " at ") AppendNum(s, pts) AppendStr(s, "pts") Log(s) ] if rot ne 0 do [ let s=vec 20; CopyString(s, " [rotation "); AppendNum(s, (rot+30)/60); // rotation in degrees AppendStr(s, "]"); Log(s); ]; let f=flags!fam; if (f<<incl eq 0) % (rot ne 0) % ((f<<ptsize ne 0)&(f<<ptsize ne pts)) do [ Log(" ...skipped*n"); return ]; if not PARCface(face) then [ Log("[??? Can't handle non-PARC style fonts!]*n"); return ] fixedPitch=f<<fixed ne 0; fLigs=f<<fligs ne 0; dLigs=f<<dligs ne 0; mQuad=f<<mquad ne 0; s40=f<<s40 ne 0; CScode=f<<CS; AppendStr(name, ".PL"); // now name is full filename outfilename=name; outstream=OpenFile(outfilename, ksTypeWriteOnly, charItem); if outstream eq 0 do [ Log("[??? Can't open output file]*n"); return ]; SetPos(instream2, psa); // position stream to beginning of segment // read charwidth array let nc=ec-bc+1; // number of chars cwLen=nc*CharWidthsize; ReadBlock(instream2, cwvec, cwLen); Log(" ..."); MakePLfile(); Closes(outstream); outstream=0; Log("OK*n"); ]; and MakePLfile() be [ // the routine that does the real work let reso=resx; // TENTHS of pixels per inch Begin(); Str("FAMILY "); Str(names!fam); End(); Begin(); Str("FACE F "); Chr(wt); Chr(sl); Chr(ex); End(); Begin(); Str("DESIGNSIZE"); DNum(pts); End(); Begin(); Str("CHECKSUM"); DNum(0); End(); Begin(); Str("CODINGSCHEME "); Str(CodeToString(CScode)); End(); Begin(); Str("SEVENBITSAFEFLAG "); Str("TRUE"); End(); Begin(); Str("RESOLUTION"); RNumOver10(reso); End(); Begin(); Str("MICASIZE"); DNum(siz); End(); Begin(); Str("UNITS "); Str("PIXELS"); End(); Begin(); Str("TEXINFO"); Cr(); let italic=sl eq $I; // for italic fonts, make slant 16% Begin(); Str("SLANT R "); Str(italic?"0.160","0.000"); // pardon my kludge End(); let xtrspace=vec 1 if s40 do [ let space,str,shr=vec 1,vec 1,vec 1; let cw=Cwp(#40); let wx=lv cw>>CharWidth.WX; let zero=vec 1; zero!0,zero!1=0,0; test fixedPitch ifnot [ let half=vec 1; // half ← 1/2 wx half!1=(wx!1)rshift 1 + (wx!0)lshift 15; half!0=(wx!0)rshift 1; space!0,space!1=wx!0,wx!1; // space ← wx DoubleAdd(space, half); // space ← space + 1/2 wx (space = 3/2 wx) str!0,str!1=wx!0,wx!1; // str ← wx shr!0,shr!1=half!0,half!1; // shr ← 1/2 wx xtrspace!0,xtrspace!1=half!0,half!1; // xtrspace ← 1/2 wx ] ifso [ space!0,space!1=wx!0,wx!1; // space ← wx xtrspace!0,xtrspace!1=wx!0,wx!1; // xtrspace ← wx str!0,str!1=0,0; // str ← 0 shr!0,shr!1=0,0; // shr ← 0 ]; Begin(); Str("SPACE"); XNum(space); End(); Begin(); Str("STRETCH"); XNum(str); End(); Begin(); Str("SHRINK"); XNum(shr); End(); ] if InFont($x) do [ let cw=Cwp($x); let xh=cw>>CharWidth.H+cw>>CharWidth.YB; Begin(); Str("XHEIGHT"); DNum(xh); End(); ] Begin(); Str("QUAD"); test mQuad ifso XNum(lv Cwp(quadchar)>>CharWidth.WX) ifnot RNumOver10(MulDiv(reso,siz,2540)); //remember reso=10*resolution End(); if s40 do [ Begin(); Str("EXTRASPACE"); XNum(xtrspace); End() ]; End(); // of TEXINFO if fLigs % dLigs do MakeLigtable(); for c=bc to ec do if InFont(c) do [ Begin(); Str("CHARACTER"); Char(c); Cr(); OutCharInfo(c); End(); ]; ] and Label(c) be [ Begin(); Str("LABEL"); Char(c); End(); ] and Lig(c1,c2) be [ Begin(); Str("LIG"); Char(c1); Char(c2); End(); ] and Kern(c,d) be [ Begin(); Str("KRN"); Char(c); DNum(d); End(); ] and Stop() be [ Begin(); Str("STOP"); End(); ] and MakeLigtable() be [ Begin(); Str("LIGTABLE"); Cr(); if fLigs do [ Label($f); let cw=Cwp($f) let wx=lv cw>>CharWidth.WX let wid=wx!0; let corr=cw>>CharWidth.W+cw>>CharWidth.XL-wid if (corr gr 0)&(sl eq $R) then [ if InFont($') then Kern($',corr) if InFont($)) then Kern($),corr) if InFont($]) then Kern($],corr) if InFont($!) then Kern($!,corr) if InFont($?) then Kern($?,corr) ] Lig($f,#6); Lig($i,#24); Lig($l,#25); Stop(); // ff, fi, fl Label(#6); cw=Cwp(#6) wx=lv cw>>CharWidth.WX wid=wx!0; corr=cw>>CharWidth.W+cw>>CharWidth.XL-wid if (corr gr 0)&(sl eq $R) then [ if InFont($') then Kern($',corr) if InFont($)) then Kern($),corr) if InFont($]) then Kern($],corr) if InFont($!) then Kern($!,corr) if InFont($?) then Kern($?,corr) ] Lig($i,#21); Lig($l,#22); Stop(); // ffi, ffl ] if dLigs do [ Label($-); Lig($-,#26); Stop(); // -- = <en dash> Label(#26); Lig($-,#23); Stop(); // <en dash>- = <em dash> ] End(); // of LIGTABLE ] and Cwp(c)=cwvec+(c-bc)*CharWidthsize; // pointer to CharWidth structure for c and InFont(c) = bc le c & c le ec & Cwp(c)>>CharWidth.H ne HNonExCode; and OutCharInfo(c) be [ let cw=Cwp(c); let wx=lv cw>>CharWidth.WX; Begin(); Str("CHARWD"); XNum(wx); End(); let ht=cw>>CharWidth.H+cw>>CharWidth.YB; Begin(); Str("CHARHT"); DNum(Max(ht,0)); End(); let dp=-cw>>CharWidth.YB; Begin(); Str("CHARDP"); DNum(Max(dp,0)); End(); let wid=wx!0; // truncate, to round italic correction up let corr=cw>>CharWidth.W+cw>>CharWidth.XL-wid; if corr gr 0 do [ Begin(); Str("CHARIC"); DNum(corr); End() ]; ]; // from fontwidths.bcpl and DecodeFace(face,w,s,e) be [ @s=(table [ $R; $I ])!(face&1) face=face rshift 1 @w=(table [ $M; $B; $L ])!(face rem 3) face=face/3 @e=(table [ $R; $C; $E ])!(face rem 3) ] and PARCface(face) = valof [ if face ls 0 then resultis false if face ge 18 then resultis false resultis true ] and CodeToString(code) =valof [ switchon code into [ case CSxeroxtext: resultis "XEROX TEXT" case CSalphabetic: resultis "ALPHABETIC" case CSpi: resultis "PI" case CSgraphic: resultis "GRAPHIC" case CSxeroxgreek: resultis "XEROX GREEK" case CSxeroxcyrillic: resultis "XEROX CYRILLIC" case CSsail: resultis "SUAI" case CSsanitizedsail: resultis "SANITIZED SUAI" ] ] and // allocate space for a new string and copy s into it MakeStringCopy(s) = valof [ let nw=WordsForString(s); // number of words required let ss=Allocate(z, nw); MoveBlock(ss, s, nw); resultis ss; ]; and Punt(string) be [ Log("*nPunt: "); Log(string); Log("*n"); CloseFiles(); abort; ];