<> <> <> DIRECTORY CIFS USING [dontCheck, Error, GetFC, OpenButDontHandleError, read], Convert USING [ValueToRope], File USING [Capability, GetSize, PageCount], Graphics USING [FontRef], GraphicsOps USING [UnsafeNewFont], Rope USING [Cat, Equal, Fetch, FromChar, ROPE, Size], Runtime USING [CallDebugger], Space USING [Create, Handle, LongPointer, Map, PageCount, virtualMemory], UserProfile USING [Number, Token], VFonts; VFontsImpl: CEDAR PROGRAM IMPORTS CIFS, Convert, File, GraphicsOps, Rope, Runtime, Space, UserProfile, VFonts EXPORTS VFonts SHARES VFonts = BEGIN OPEN VFonts; defaultFont: PUBLIC VFonts.Font _ NIL; defaultGFont: PUBLIC Graphics.FontRef _ NIL; Error: PUBLIC ERROR [code: VFonts.ErrorCode] = CODE; Strike: TYPE = LONG POINTER TO StrikeObject; StrikeObject: TYPE = RECORD [header: StrikeHeader, body: StrikeBody]; StrikeHeader: TYPE = RECORD [ bits: StrikeBits, min: CARDINAL, max: CARDINAL, maxwidth: CARDINAL]; StrikeBits: TYPE = RECORD [ newStyle: BOOL, indexed: BOOL, fixed: BOOL, kerned: BOOL, pad: [0..7777B]]; BoundingBox: TYPE = RECORD [ FontBBox, FontBBoy, FontBBdx, FontBBDy: INTEGER]; StrikeBody: TYPE = RECORD [ length: CARDINAL, ascent: CARDINAL, descent: CARDINAL, xoffset: CARDINAL, raster: CARDINAL, bitmap: ARRAY [0..0) OF WORD]; <> StringWidth: PUBLIC PROC [string: Rope.ROPE, font: VFonts.Font _ defaultFont] RETURNS [width: [0..LAST[INTEGER]]] = BEGIN width _ 0; FOR i: INT IN [0..Rope.Size[string]) DO width _ width + VFonts.CharWidth[Rope.Fetch[string, i], font] ENDLOOP; END; Initialize: PROC [font: VFonts.Font] = TRUSTED BEGIN strike: Strike = font.address; c: CHARACTER; bits: StrikeBits = strike.header.bits; IF ~bits.newStyle THEN ERROR Error[illegalFormat]; IF bits.indexed THEN ERROR Error[illegalFormat]; font.kerned _ bits.kerned; font.min _ LOOPHOLE[strike.header.min]; font.max _ LOOPHOLE[strike.header.max]; IF font.min NOT IN [0C..177C] THEN ERROR Error[illegalFormat]; IF font.max NOT IN [0C..177C] THEN ERROR Error[illegalFormat]; font.ascent _ strike.body.ascent; font.height _ strike.body.ascent + strike.body.descent; font.raster _ strike.body.raster; font.maxWidth _ strike.header.maxwidth; SetFontAddresses[font]; FOR c IN [font.min..MIN[font.max, 176C]] DO font.width[c] _ XInSegment[c+1, font] - XInSegment[c, font] ENDLOOP; FOR c IN [0C..font.min) DO font.width[c] _ font.width[font.min] ENDLOOP; FOR c IN (MIN[font.max, 176C]..177C] DO font.width[c] _ font.width[font.min] ENDLOOP; font.width[15C] _ font.width[40C]; -- CR width hack END; SetFontAddresses: PROC [font: Font] = TRUSTED INLINE BEGIN strike: Strike = font.address; font.bitmap _ @strike.body.bitmap + (IF font.kerned THEN SIZE[BoundingBox] ELSE 0); font.xInSegment _ LOOPHOLE[font.bitmap, LONG POINTER] + font.raster*font.height - LOOPHOLE[font.min, INTEGER]; END; fontChain: Font _ NIL; EstablishFont: PUBLIC PROC [family: Rope.ROPE, size: CARDINAL, bold: BOOL _ FALSE, italic: BOOL _ FALSE, defaultOnFailure: BOOL _ TRUE] RETURNS [font: Font] = BEGIN IF size#10 AND Rope.Equal[family, "Tioga", FALSE] THEN family _ "TimesRoman"; <> FOR font _ fontChain, font.next UNTIL font=NIL DO IF size=font.size AND bold=font.bold AND italic=font.italic AND Rope.Equal[family, font.family, FALSE] THEN RETURN [font]; ENDLOOP; RETURN[NewFont[family, size, bold, italic, defaultOnFailure]]; END; NewFont: PROC [family: Rope.ROPE, size: CARDINAL, bold, italic, defaultOnFailure: BOOL] RETURNS [font: Font] = BEGIN trialName: Rope.ROPE; IF bold OR italic THEN BEGIN trialName _ Rope.Cat[trialName, family]; IF size#0 THEN trialName _ Rope.Cat[trialName, Convert.ValueToRope[[unsigned[size]]]]; IF bold THEN trialName _ Rope.Cat[trialName, Rope.FromChar['B]]; IF italic THEN trialName _ Rope.Cat[trialName, Rope.FromChar['I]]; trialName _ Rope.Cat[trialName, ".strike"]; IF (font _ LoadFont[trialName]) = NIL THEN BEGIN -- try to match size&family FOR f: Font _ fontChain, f.next UNTIL f=NIL DO IF size=f.size AND ~f.bold AND ~f.italic AND Rope.Equal[family, f.family, FALSE] THEN { font _ CopyFont[f]; IF bold THEN font.synthBold _ TRUE; IF italic THEN font.synthItalic _ TRUE; EXIT}; ENDLOOP; END; IF font=NIL THEN -- lose, give up {IF ~defaultOnFailure THEN Error[fontNotFound] ELSE trialName _ NIL} ELSE IF font.gFont=NIL THEN TRUSTED { font.gFont _ LOOPHOLE[GraphicsOps.UnsafeNewFont[font.address]]}; END; IF font=NIL THEN BEGIN -- no luck yet trialName _ Rope.Cat[trialName, family]; IF size#0 THEN trialName _ Rope.Cat[trialName, Convert.ValueToRope[[unsigned[size]]]]; trialName _ Rope.Cat[trialName, ".strike"]; IF (font _ LoadFont[trialName])=NIL THEN -- lose {IF ~defaultOnFailure THEN Error[fontNotFound] ELSE font _ CopyFont[defaultFont]} ELSE TRUSTED {font.gFont _ LOOPHOLE[GraphicsOps.UnsafeNewFont[font.address]]}; IF bold THEN font.synthBold _ TRUE; IF italic THEN font.synthItalic _ TRUE; END; font.family _ family; font.size _ size; font.bold _ bold; font.italic _ italic; font.next _ fontChain; fontChain _ font; IF font.size=10 AND ~font.bold AND ~font.italic AND Rope.Equal[font.family, "Tioga", FALSE] THEN font.ascent _ 9; -- horrible patch to compensate for bad baseline END; LoadFont: PROC [fileName: Rope.ROPE] RETURNS [font: Font] = TRUSTED BEGIN file: File.Capability; pages: File.PageCount; size: Space.PageCount; space: Space.Handle; IF Rope.Fetch[fileName] # '/ THEN fileName _ Rope.Cat[fontDir, fileName]; file _ CIFS.GetFC[CIFS.OpenButDontHandleError[fileName, CIFS.read+CIFS.dontCheck ! CIFS.Error => CHECKED {GOTO Punt}]]; pages _ File.GetSize[file]; size _ IF pages<20 THEN pages ELSE ERROR; space _ Space.Create[size: size, parent: Space.virtualMemory]; Space.Map[space: space, window: [file: file, base: 1]]; -- first page is 1! font _ NEW[FontObject]; font.address _ Space.LongPointer[space]; Initialize [font]; EXITS Punt => {RETURN[NIL]} END; CopyFont: PROC [font: Font] RETURNS [Font] = INLINE {RETURN[NEW[FontObject _ font^]]}; CreateDefaultFont: PROC = BEGIN err: BOOL _ FALSE; defaultFamily: Rope.ROPE _ UserProfile.Token["DefaultFontFamily", "Tioga"]; defaultSize: CARDINAL _ UserProfile.Number["DefaultFontSize", 10]; defaultFont _ EstablishFont[defaultFamily, defaultSize, FALSE ! ANY => {err _ TRUE; CONTINUE}]; IF err AND (defaultSize#10 OR ~Rope.Equal["Tioga", defaultFamily, FALSE]) THEN BEGIN <> err _ FALSE; defaultFont _ EstablishFont["Tioga", 10, FALSE ! CIFS.Error => CHECKED {err _ TRUE; CONTINUE}]; END; IF err THEN BEGIN -- try IVY err _ FALSE; defaultFont _ EstablishFont["/Ivy/Tioga/StrikeFonts/Tioga", 10, FALSE ! CIFS.Error => CHECKED {err _ TRUE; CONTINUE}]; END; IF err THEN DO TRUSTED {Runtime.CallDebugger["Couldn't get any fonts!"L]}; ENDLOOP; defaultGFont _ defaultFont.gFont; END; fontDir: Rope.ROPE = "/Indigo/Tioga/StrikeFonts/"; CreateDefaultFont[]; END.