// R E A D A L (PREPRESS) // // ReadAl -- font editor for low resolution fonts. // //Bcpl/f ReadAl.bcpl // //Last modified February 7, 1980 11:41 AM by Kerry A. LaPrade, XEOS // // Reads a *.AL file, and produces a Chars file with the same bits. Claimed // to be a one-sided inverse to MakeAL, in the sense that ReadAL followed // by MakeAL should be the identity. Since *.AL format doesn't include // as much information as *.AC, some guesses and approximations have to be // made in the *.AC output. get "ix.dfs" // outgoing procedures external [ ReadAL GuessData ] // incoming procedures external [ //PREPRESSWINDOW WindowRead WindowReadBlock WindowWrite WindowWriteBlock WindowGetPosition WindowSetPosition WindowCopy WindowClose //PREPRESS PrePressWindowInit //PRESSML MulDiv //SCAN ReadCom TypeForm StrCop //PREPRESSUTIL FSGetX FSPut TypeChar DPCop DblShift GetPosRelative WriteIXTempFile Scream NoFile //MICROFLOAT DPSB DPSHR //OS Noop DoubleAdd Zero SetBlock MoveBlock Usc Allocate FileLength ] // incoming statics external [ @prePressZone ] // internal statics static [ @SmallAL @ALf @ALBuf @ALBufpos @DPzero @DPone @DPtemp ] // File-wide structure and manifest declarations. structure str: [ len byte char↑1,255 byte ] structure ALH: [ //the two header words of an AL file Height word proportional bit baseline bit 7 maxWidth bit 8 ] structure XHdataH: [ //the two words of header for each char in an AL file XW word HD byte XH byte ] manifest [ FHEADLen= 1 ALHLen=size ALH/16 //which is 2 XHdataHLen=size XHdataH/16 //another name for 2 ] let ReadAL(inputName,outputName;numargs na) be [ReadAL DPzero=table [ 0;0 ] DPone=table [ 0;1 ] DPtemp=table [ 0;0 ] let tempStr = vec 20 if na eq 0 then [ ReadCom(tempStr) inputName=tempStr outputName="ACtemp" ] let scrf=PrePressWindowInit(0) ALWindowInit(inputName) //read the AL file header ALWindowSetPosition(DPzero) let inhead= vec ALHLen ALWindowReadBlock(inhead,ALHLen) let inh=inhead>>ALH.Height let inbase=inhead>>ALH.baseline let inxmax=inhead>>ALH.maxWidth //set font bounding box params, except for FBBdx (can't tell that one yet) let FBBdy=inh let FBBoy=inbase-FBBdy let FBBox=0 //now, figure out what beginning and ending character in .AL are let ch=0 while DummyChar(ch) do ch=ch+1 let bc=ch ch=255 while DummyChar(ch) do ch=ch-1 let ec=ch if ec ls bc then Scream("empty Alto font") let nc=ec-bc+1 //OK, time to write the output file header let CharsIX = vec IXLChars let NameIX = vec IXLName Zero(CharsIX, IXLChars) Zero(NameIX, IXLName) CharsIX>>IX.Type=IXTypeChars CharsIX>>IX.Length=IXLChars CharsIX>>IX.bc=bc CharsIX>>IX.ec=ec CharsIX>>IX.resolutionx=720 CharsIX>>IX.resolutiony=720 CharsIX>>IX.fam=1 NameIX>>IXN.Type=IXTypeName NameIX>>IXN.Length=IXLName NameIX>>IXN.Code=1 //now, guess the font data from the .AL file name let famguess=vec 20 let faceguess= 0 let sizeguess= 0 GuessData(inputName, lv famguess, lv faceguess, lv sizeguess) StrCop(famguess, lv NameIX>>IXN.Name) CharsIX>>IX.face=faceguess CharsIX>>IX.siz=sizeguess //now, all the IX data is set except for .sa and .len WriteIXTempFile(scrf,NameIX,CharsIX) //and that took care of .sa as well; must handle .len later on //scratch stream is left at beginning of CharacterSegment let CharWidths=FSGetX(nc*CharWidthsize) let CharPtrs=FSGetX(nc*2) //and we will write the rasters right into the file let WidthsPos=vec 1 WindowGetPosition(scrf, WidthsPos) let PtrsPos=vec 1 PtrsPos!0=0; PtrsPos!1=nc*CharWidthsize DoubleAdd(PtrsPos, WidthsPos) let RastersPos=vec 1 RastersPos!0=0; RastersPos!1=nc*2 DoubleAdd(RastersPos, PtrsPos) WindowSetPosition(scrf, RastersPos) //one character at a time now, putting rasters into scrf for chrel=0 to nc-1 do [nextchar ch=chrel+bc test DummyChar(ch) ifso [ let thisWidth= CharWidths+chrel*CharWidthsize Zero(thisWidth,CharWidthsize) thisWidth>>CharWidth.H=HNonExCode let thisPtr= CharPtrs+chrel*2 thisPtr!0=-1; thisPtr!1=-1 //and no rasters to output, so we're done ] ifnot [ TypeChar(ch) //Our first task will be to get the character plus all of its extensions //into an array in Alto display order. let xmaxW= (inxmax+16)/16 //(Note that bits can overhang the nominal character width // by up to one full word, corresponding to XW=1.) let ALbits=FSGetX(inh*xmaxW) Zero(ALbits, inh*xmaxW) let BBdx, chwidth, BBox=0,0,0 let chblanktop, chblankbot = 1000, 1000 let ptrpos, hdrpos, datapos=vec 1, vec 1, vec 1 ptrpos!0=0; ptrpos!1=ch+ALHLen //now that we have pointer to first XHdata block, enter a loop for col=0 to xmaxW-1 do [ //nextext ALWindowSetPosition(ptrpos) hdrpos!0=0; hdrpos!1=ALWindowRead() DoubleAdd(hdrpos, ptrpos) //pointer is self-relative ALWindowSetPosition(hdrpos) let hdr=vec XHdataHLen ALWindowReadBlock(hdr, XHdataHLen) let numlines=hdr>>XHdataH.XH let blanklines=hdr>>XHdataH.HD let xw=hdr>>XHdataH.XW if blanklines ls chblanktop then chblanktop=blanklines if (inh-blanklines-numlines) ls chblankbot then chblankbot=(inh-blanklines-numlines) DPtemp!0=0; DPtemp!1=numlines DPCop(datapos, hdrpos) DPSB(datapos, DPtemp) ALWindowSetPosition(datapos) for i=blanklines to blanklines+numlines-1 do [ ALbits!(i*xmaxW + col)=ALWindowRead() ] BBdx=BBdx+16 test (xw&1) eq 0 ifso [ ptrpos!1=(xw rshift 1)+ALHLen; chwidth=chwidth+16; loop ] ifnot [ chwidth=chwidth+(xw rshift 1); break ] ] //nextext //the ALbits array now holds the character in Alto format //The values of BBdx and BBox are generous estimates, which we will // update later. But we can now determine the BBoy and BBdy values. let BBoy=FBBoy+chblankbot let BBdy=inh-chblanktop-chblankbot //Take the ALbits array, and rotate it into the Charbits array let BBdyW=(BBdy+15)/16 let Chardata=FSGetX(BBdx*BBdyW+FHEADLen) Zero(Chardata, BBdx*BBdyW+FHEADLen) let Charbits=Chardata+FHEADLen let BitsSeen=false let FirstNonEmptyScanLine=nil let LastNonEmptyScanLine= -1 for i=0 to BBdx-1 do [ //nextscanline let empty = true for j=0 to BBdy-1 do [ //nextdatabit if GetBit(ALbits+(inbase-BBoy-j-1)*xmaxW+(i rshift 4), i&15) then [ empty = false SetBit(Charbits+i*BBdyW+(j rshift 4), j&15) ] ] //nextdatabit unless BitsSeen % empty do [ FirstNonEmptyScanLine=i BitsSeen=true ] unless empty do LastNonEmptyScanLine=i ] //nextscanline FSPut(ALbits) //Now, we can compute the real BBdx and BBox test BitsSeen ifso [ BBdx=LastNonEmptyScanLine-FirstNonEmptyScanLine+1 BBox=FirstNonEmptyScanLine ] ifnot [ BBdx=0 BBox=0 BBdy=0; BBdyW=0 ] //If BBox is now non-zero, we must shift the bits backward to wipe out //the early all-zero scan lines if BBox ne 0 then MoveBlock(Charbits, Charbits+BBox*BBdyW, BBdx*BBdyW) //Lets fill in the width data Chardata>>FHEAD.hw=BBdyW Chardata>>FHEAD.ns=BBdx let thisWidth = CharWidths+chrel*CharWidthsize thisWidth!0=chwidth; thisWidth!1=0 //set the x width fraction thisWidth!2=0; thisWidth!3=0 //set the y width fraction thisWidth>>CharWidth.XL=BBox thisWidth>>CharWidth.YB=BBoy thisWidth>>CharWidth.W=BBdx thisWidth>>CharWidth.H=BBdy //And the relative position of the rasters let thisPtr = CharPtrs+chrel*2 WindowGetPosition(scrf, thisPtr) DPSB(thisPtr, PtrsPos) //pointer is relative to beginning of Ptrs //and then store the resulting rasters WindowWriteBlock(scrf, Chardata, BBdx*BBdyW+FHEADLen) FSPut(Chardata) TypeForm(0) ] ]nextchar //clean up at the end ALWindowClose() let tlen=vec 1 WindowGetPosition(scrf, tlen) GetPosRelative(scrf, lv CharsIX>>IX.sa, lv CharsIX>>IX.len) WindowSetPosition(scrf, DPzero) WriteIXTempFile(scrf, NameIX, CharsIX) WindowWriteBlock(scrf, CharWidths, nc*CharWidthsize) FSPut(CharWidths) WindowWriteBlock(scrf, CharPtrs, nc*2) FSPut(CharPtrs) //copy scratch file to real output file let ACf=PrePressWindowInit(outputName) WindowSetPosition(scrf,DPzero) WindowSetPosition(ACf,DPzero) WindowCopy(scrf,ACf,tlen) WindowClose(ACf,-1) WindowClose(scrf) TypeForm(4, tlen, " is length (words) of Chars output file.") ]ReadAL and SetBit(lvword, bitnum) be [ @lvword=(@lvword)%(1 lshift (15-bitnum)) ] and GetBit(lvword, bitnum) = ((@lvword) rshift (15-bitnum))&1 and DummyChar(ch,ALf) = valof [ let ptrpos, datapos=vec 1, vec 1 ptrpos!0=0; ptrpos!1=ch+ALHLen ALWindowSetPosition(ptrpos) datapos!0=0 datapos!1=ALWindowRead() DoubleAdd(datapos, ptrpos) ALWindowSetPosition(datapos) let x=vec XHdataHLen ALWindowReadBlock(x, XHdataHLen) if x>>XHdataH.XW ne 1 then resultis(false) if x>>XHdataH.XH ne 0 then resultis(false) if x>>XHdataH.HD ne 0 then resultis(false) resultis(true) ] and GuessData(inputName, lvfam, lvfac, lvsiz) be [ //set up defaults, in case the .AL name doesn't parse StrCop("UNKNOWN", @lvfam) @lvsiz=0 @lvfac=0 let ucinputName=vec 20 StrCop(inputName, ucinputName) UpperCaseify(ucinputName) //guess the family name let fam=vec 20 fam>>str.len=0 let i=-1 for ip=1 to ucinputName>>str.len do [ test ucLetter(ucinputName>>str.char↑ip) ifso Putstr(ucinputName>>str.char↑ip, fam) ifnot [ i=ip; break ] ] test fam>>str.len eq 0 ifso return ifnot StrCop(fam, @lvfam) if i eq -1 then return //now, guess the point size let ptsiz=0 for ip=i to ucinputName>>str.len do [ test Digit(ucinputName>>str.char↑ip) ifso ptsiz=10*ptsiz+(ucinputName>>str.char↑ip - $0) ifnot [ i=ip; break ] ] if (ptsiz le 0) % (ptsiz ge 301) then return @lvsiz=MulDiv(ptsiz, 2540, 72) //compute size in micas //now, guess the face let fac=0 if ucinputName>>str.char↑i eq $B then [ fac=fac+2 i=i+1 ] if ucinputName>>str.char↑i eq $I then [ fac=fac+1 i=i+1 ] @lvfac=fac //hopefully, the remaining characters of the ucinputName are ".AL", but //why bother checking? ] //of GuessData and UpperCaseify(string) be for i=1 to string>>str.len do [ let char=string>>str.char↑i if (char ge $a) & (char le $z) then string>>str.char↑i=char+$A-$a ] and ucLetter(char)= (char ge $A) & (char le $Z) and Digit(char)= (char ge $0) & (char le $9) and Putstr(char, string) be [ let l=string>>str.len string>>str.len=l+1 string>>str.char↑(l+1)=char ] //The ALWindow package that follows implements the following conventions. //ALWindowInit checks the size of the AL file, and, if it is less than half //of available storage, sets the SmallAL flag, and reads the entire file into //core beginning at ALBuf. Then, ALBufpos is used as a stream pointer to //that buffer region. If the AL file is large, then all operations really //go to the file (and ReadAL runs like molasses in January...) and ALWindowInit(name) be [ ALf=PrePressWindowInit(name) if ALf eq 0 then [ NoFile(name); Scream(" Giving up..."); abort ] let len=vec 1 FileLength(ALf, len) WindowSetPosition(ALf, DPzero) DblShift(len,1) //change units from bytes to words let freespace=nil Allocate(prePressZone, 77777b, lv freespace) test len!0 eq 0 ifnot [ SmallAL=false ] ifso [ let comp=Usc(len!1,freespace/2) test comp eq 1 ifso [ SmallAL=false ] ifnot [ SmallAL=true ] ] test SmallAL ifso [ ALBuf=FSGetX(len!1+1) WindowReadBlock(ALf, ALBuf, len!1+1) WindowClose(ALf) ALBufpos=ALBuf TypeForm("AL was small; freespace was: ", 10, freespace,0) TypeForm("and AL len was: ", 4, len, 0) ] ifnot [ TypeForm("AL was large; freespace was: ", 10, freespace,0) TypeForm("and AL len was: ", 4, len, 0) ] ] and ALWindowClose() be test SmallAL ifso [ FSPut(ALBuf) ] ifnot [ WindowClose(ALf) ] and ALWindowGetPosition(pos) be test SmallAL ifso [ pos!0=0 pos!1=ALBufpos ] ifnot [ WindowGetPosition(ALf, pos) ] and ALWindowSetPosition(pos) be test SmallAL ifso [ if pos!0 ne 0 then Scream("Attempt to set position beyond end of AL file") ALBufpos=ALBuf+pos!1 ] ifnot [ WindowSetPosition(ALf, pos) ] and ALWindowRead() = valof [ test SmallAL ifso [ let temp=ALBufpos!0 ALBufpos=ALBufpos+1 resultis(temp) ] ifnot [ resultis(WindowRead(ALf)) ] ] and ALWindowReadBlock(data, len) be test SmallAL ifso [ MoveBlock(data,ALBufpos,len) ALBufpos=ALBufpos+len ] ifnot [ WindowReadBlock(ALf, data, len) ] and ALWindowWrite(data) be test SmallAL ifso [ ALBufpos!0=data ALBufpos=ALBufpos+1 ] ifnot [ WindowWrite(ALf, data) ] and ALWindowWriteBlock(data, len) be test SmallAL ifso [ MoveBlock(ALBufpos,data,len) ALBufpos=ALBufpos+len ] ifnot [ WindowWriteBlock(ALf, data, len) ]