-- PDTextBitmapImpl.mesa
-- Copyright (C) 1984, 1986 Xerox Corporation.  All rights reserved.
-- Michael Plass, September 26, 1984 5:33:47 pm PDT
-- Tim Diebert,  5-Sep-86 14:09:16
-- 
DIRECTORY Environment, PDInterpBitmap, PDInterpSysCalls, PDTextBitmap,
   Stream, PDStrikeFormat, PDRemoteStream, Inline, String;
   
PDTextBitmapImpl: PROGRAM
   IMPORTS PDInterpBitmap, PDInterpSysCalls, PDRemoteStream, Stream,
      Inline, String
   EXPORTS PDTextBitmap = BEGIN
   
fontName: PUBLIC LONG STRING ← [120];
bc: CHAR = ' ;
ec: CHAR = '~;
charBitmap: ARRAY CHAR[bc..ec] OF PDInterpBitmap.BitmapDesc;
charWidth: ARRAY CHAR[bc..ec] OF INTEGER;
fontAscent: INTEGER ← 0;
fontDescent: INTEGER ← 0;
ksFontBuffer: LONG POINTER ← NIL;
bytesPerWord: NAT = Environment.bytesPerWord;
nilBitmap: PDInterpBitmap.BitmapDesc = [
   sOrigin: 0,
   fOrigin: 0,
   sMin: 0,
   fMin: 0,
   sSize: 0,
   fSize: 0,
   pointer: NIL,
   rast: 0,
   lines: 0
   ];
   
BadFont: ERROR = CODE;
   
SetFont: PUBLIC PROC [fontFileName: LONG STRING, userName, password: LONG STRING] RETURNS [ok: BOOLEAN ← FALSE] = {
   cr: LONG STRING ← [40];
   nameCopy: LONG STRING ← [120];
   NameCopy: PROC RETURNS [LONG STRING] = {
      String.Copy[nameCopy, fontFileName];
      RETURN [nameCopy]
      };
   bytes: INT ← PDRemoteStream.Lookup[NameCopy[], cr, userName, password];
   newBuffer: LONG POINTER ← NIL;
   bytesRead: INT ← 0;
   readFile: PROC[stream: Stream.Handle] = {
      bytesRead ← Stream.GetBlock[stream, [blockPointer: newBuffer, startIndex: 0, stopIndexPlusOne: bytes]].bytesTransferred;
      };
   IF ksFontBuffer # NIL THEN {
      FOR c: CHAR IN [bc..ec] DO charBitmap[c] ← nilBitmap; charWidth[c] ← 0 ENDLOOP;
      PDInterpSysCalls.FreeSpace[ksFontBuffer];
      fontAscent ← fontDescent ← 0;
      ksFontBuffer ← NIL
      };
   fontName.length ← 0;
   newBuffer ← PDInterpSysCalls.AllocateSpace[(bytes+bytesPerWord-1)/bytesPerWord];
   PDRemoteStream.Read[fontFileName, userName, password, readFile];
   IF bytesRead # bytes THEN {
      PDInterpSysCalls.FreeSpace[newBuffer];
      RETURN [FALSE]
      };
   ok ← TRUE;
   ParseFont[fontFileName, newBuffer, bytes
      -- ! BadFont => {ok ← FALSE; CONTINUE};
      ];
   IF ok THEN {
      ksFontBuffer ← newBuffer;
      }
   ELSE {
      FOR c: CHAR IN [bc..ec] DO charBitmap[c] ← nilBitmap; charWidth[c] ← 0 ENDLOOP;
      PDInterpSysCalls.FreeSpace[newBuffer];
      fontAscent ← fontDescent ← 0;
      String.Copy[to: fontName, from: fontFileName];
      };
   };
   
ParseFont: PUBLIC PROC [fontFileName: LONG STRING, buffer: LONG POINTER, bytes: INT] = {
   wordIndex: INT ← 0;
   ReadBlock: PROC [dest: LONG POINTER, words: CARDINAL, wordOffset: INT ← -1] = {
      IF wordOffset >= 0 THEN wordIndex ← wordOffset;
      Inline.LongCOPY[from: buffer+wordIndex, nwords: words, to: dest];
      wordIndex ← wordIndex + words;
      };
   strike: PDInterpBitmap.BitmapDesc ← nilBitmap;
   defaultCharBitmap: PDInterpBitmap.BitmapDesc ← nilBitmap;
   defaultCharWidth: INTEGER ← 0;
   header: PDStrikeFormat.Header;
   ReadBlock[@header, SIZE[PDStrikeFormat.Header], 0];
   IF header.format.oneBit # T THEN ERROR BadFont;
   IF header.format.index # F THEN ERROR BadFont;
   IF header.format.unused # 0 THEN ERROR BadFont;
   IF header.format.kerned = T THEN {
      boundingBox: PDStrikeFormat.BoundingBox;
      body: PDStrikeFormat.Body;
      xInSegment, prevXInSegment: CARDINAL;
      xInSegmentOffset: INT;
      widthEntryOffset: INT;
      widthEntry: PDStrikeFormat.WidthEntry;
      bodyOffset: INT;
      ReadBlock[@boundingBox, SIZE[PDStrikeFormat.BoundingBox]];
      bodyOffset ← wordIndex;
      ReadBlock[@body, SIZE[PDStrikeFormat.Body]];
      fontAscent ← body.ascent;
      fontDescent ← body.descent;
      strike ← [
         sOrigin: -body.ascent,
         fOrigin: 0,
         sMin: 0,
         fMin: 0,
         sSize: body.ascent+body.descent,
         fSize: body.raster*Environment.bitsPerWord,
         pointer: buffer+wordIndex,
         rast: body.raster,
         lines: body.ascent+body.descent
         ];
      wordIndex ← wordIndex + strike.rast*strike.lines;
      widthEntryOffset ← wordIndex+(header.max-header.min+3)*SIZE[CARDINAL];
      IF widthEntryOffset # bodyOffset + body.length THEN ERROR BadFont;
      ReadBlock[@prevXInSegment, SIZE[CARDINAL]];
      xInSegmentOffset ← wordIndex;
      FOR char: CHAR IN [header.min..header.max] DO
         charPixels: PDInterpBitmap.BitmapDesc ← strike;
         ReadBlock[@xInSegment, SIZE[CARDINAL], xInSegmentOffset];
         xInSegmentOffset ← xInSegmentOffset + SIZE[CARDINAL];
         ReadBlock[@widthEntry, SIZE[PDStrikeFormat.WidthEntry], widthEntryOffset];
         widthEntryOffset ← widthEntryOffset + SIZE[PDStrikeFormat.WidthEntry];
         IF widthEntry # PDStrikeFormat.nullWidthEntry THEN {
            IF widthEntry.width > header.maxwidth THEN ERROR BadFont;
            charPixels.fOrigin ← widthEntry.offset+boundingBox.fbbox-prevXInSegment;
            charPixels.fMin ← prevXInSegment;
            charPixels.fSize ← xInSegment-prevXInSegment;
            IF char IN [bc..ec] THEN {
               charBitmap[char] ← charPixels;
               charWidth[char] ← widthEntry.width;
               };
            };
         prevXInSegment ← xInSegment;
         ENDLOOP;
      ReadBlock[@xInSegment, SIZE[CARDINAL], xInSegmentOffset];
      xInSegmentOffset ← xInSegmentOffset + SIZE[CARDINAL];
      ReadBlock[@widthEntry, SIZE[PDStrikeFormat.WidthEntry], widthEntryOffset];
      widthEntryOffset ← widthEntryOffset + SIZE[PDStrikeFormat.WidthEntry];
      IF widthEntry # PDStrikeFormat.nullWidthEntry THEN {
         strike.fOrigin ← widthEntry.offset+boundingBox.fbbox-prevXInSegment;
         strike.fMin ← prevXInSegment;
         strike.fSize ← xInSegment-prevXInSegment;
         defaultCharBitmap ← strike;
         defaultCharWidth ← widthEntry.width;
         }
      ELSE ERROR BadFont;
      }
   ELSE ERROR BadFont;
   FOR char: CHAR IN [bc..ec] DO
      IF charBitmap[char].pointer = NIL THEN {
         charBitmap[char] ← defaultCharBitmap;
         charWidth[char] ← defaultCharWidth;
         };
      ENDLOOP;
   IF wordIndex*bytesPerWord # bytes THEN ERROR BadFont;
   };
   
FontAscent: PUBLIC PROC RETURNS [INTEGER] = {RETURN [fontAscent]};
   
FontDescent: PUBLIC PROC RETURNS [INTEGER] = {RETURN [fontDescent]};
   
TextWidth: PUBLIC PROC [string: LONG STRING, start: NAT ← 0, length: NAT ← NAT.LAST] RETURNS [textWidth: INTEGER] = {
   textWidth ← 0;
   FOR i: NAT IN [start..start+length) DO
      IF i >= string.length THEN EXIT
      ELSE {
         char: CHAR ← string[i];
         IF char IN [bc..ec] THEN textWidth ← textWidth + charWidth[char];
         };
      ENDLOOP;
   };
   
TextToBitmap: PUBLIC PROC [dest: PDInterpBitmap.BitmapDesc, string: LONG STRING, start: NAT ← 0, length: NAT ← NAT.LAST, function: PDInterpBitmap.Function] = {
   w: INTEGER ← 0;
   FOR i: NAT IN [start..start+length) DO
      IF i >= string.length THEN EXIT
      ELSE {
         char: CHAR ← string[i];
         IF char IN [bc..ec] THEN {
            bitmap: PDInterpBitmap.BitmapDesc ← charBitmap[char];
            bitmap.fOrigin ← bitmap.fOrigin + w;
            PDInterpBitmap.Transfer[dest, bitmap, function];
            w ← w + charWidth[char];
            };
         };
      ENDLOOP;
   };
   
END.