<> <> <> DIRECTORY Ascii, Atom, Environment, Real, RefText, Rope, SafeStorage, Scaled, UFFontDirReader, UnifiedFonts, UFFileManager, MessageWindow, IO; UnifiedFontsImpl: CEDAR MONITOR IMPORTS Atom, RefText, Rope, SafeStorage, Scaled, UFFontDirReader, UFFileManager, MessageWindow, IO EXPORTS UnifiedFonts = BEGIN OPEN UnifiedFonts; BadFontFileFormat: PUBLIC ERROR = CODE; pZone: PUBLIC ZONE _ SafeStorage.NewZone[initialSize: 0]; rotationFuzz: REAL = 1/3600.0; fontDirFileName: ROPE _ "Default.FontDir"; fontHead: FONT _ NIL; -- all FONTs are linked together, starting here. defaultFont: FONT; DefaultFont: PROCEDURE RETURNS [font: FONT] = { formattingClassInit: REF initProcRec _ NARROW[Atom.GetProp[atom: $Strike, prop: $FontFormattingClass]]; graphicsClassInit: REF initProcRec _ NARROW[Atom.GetProp[atom: $Strike, prop: $FontGraphicsClass]]; IF defaultFont#NIL THEN RETURN [defaultFont]; font _ pZone.NEW[FontRec]; font.name _ "Default"; font.requestedTransformation _ font.actualTransformation _ [10, 0]; font.deviceType _ $Screen; font.nsCodeTable _ UFFileManager.KeyOf["Ascii.codeScheme"]; font.formattingKey _ font.graphicsKey _ UFFileManager.KeyOf["[Indigo]Strikefonts>Tioga10.strike"]; formattingClassInit.init[font]; graphicsClassInit.init[font]; InitFontBoundingBox[font]; font.bitmapCacheable _ Area[font.fontBoundingBox] candidateMaxSize THEN RETURN [FALSE]; IF NOT name.Equal[RefText.TrustTextAsRope[candidateFontName], FALSE] THEN RETURN [FALSE]; IF candidateRotation >= 0 AND ABS[candidateRotation - transformation.rotation] >= rotationFuzz THEN RETURN [FALSE]; RETURN [TRUE]; }; RememberFont: ENTRY PROC = {font.link _ fontHead; fontHead _ font}; SaveResult: UFFontDirReader.ResultProc = { formattingClassInit: REF initProcRec _ NARROW[Atom.GetProp[atom: metricsType, prop: $FontFormattingClass]]; graphicsClassInit: REF initProcRec _ NARROW[Atom.GetProp[atom: graphicsType, prop: $FontGraphicsClass]]; font _ pZone.NEW[FontRec]; font.name _ CopyRope[name]; -- So it is OK to trust a REF TEXT as a ROPE font.requestedTransformation _ font.actualTransformation _ transformation; IF candidateSize # 0.0 THEN font.actualTransformation.scale _ candidateSize; IF candidateRotation >= 0 THEN font.actualTransformation.rotation _ candidateRotation; font.deviceType _ candidateDeviceType; font.nsCodeTable _ codeScheme; font.formattingKey _ metricsName; font.graphicsKey _ graphicsName; formattingClassInit.init[font]; graphicsClassInit.init[font]; InitFontBoundingBox[font]; font.bitmapCacheable _ Area[font.fontBoundingBox]> msg: ROPE _ IO.PutFToRope[ "Default substituted for %g font %g at %gbp, %g", IO.rope[Atom.GetPName[deviceType]], IO.rope[CopyRope[name]], IO.real[transformation.scale], IO.real[transformation.rotation] ]; MessageWindow.Append[msg, TRUE]; font _ DefaultFont[]; }; }; InitFontBoundingBox: PROCEDURE [font: FONT] = { bb: Box _ [0,0,0,0]; FOR c: CHAR IN [font.bc..font.ec] DO cbb: Box _ BoundingBox[font, c]; bb.xmin _ MIN[bb.xmin, cbb.xmin]; bb.ymin _ MIN[bb.ymin, cbb.ymin]; bb.xmax _ MAX[bb.xmax, cbb.xmax]; bb.ymax _ MAX[bb.ymax, cbb.ymax]; ENDLOOP; font.fontBoundingBox _ bb; }; EasyTransformations: PUBLIC PROCEDURE [fontName: REF, deviceType: DeviceType, transformationProc: TransformationProc] = { name: ROPE _ IF ISTYPE[fontName, REF TEXT] THEN TrustAsRope[fontName] ELSE NARROW[fontName]; TestForMatch: UFFontDirReader.ExamineProc = { IF deviceType # candidateDeviceType THEN RETURN [FALSE]; IF NOT name.Equal[RefText.TrustTextAsRope[candidateFontName], FALSE] THEN RETURN [FALSE]; IF candidateSize=0 AND candidateRotation=-360 THEN candidateRotation_0; RETURN [transformationProc[[scale: candidateSize, rotation: candidateRotation]]]; }; IgnoreResult: UFFontDirReader.ResultProc = {quit _ TRUE}; UFFontDirReader.EnumerateFontDirectory[fontDirFileName, TestForMatch, IgnoreResult]; }; GetPressFontSpecification: PUBLIC PROCEDURE [font: FONT] RETURNS[pfs: PressFontSpecification] = { }; GetLigatureOrKern: PUBLIC PROCEDURE [font: FONT, char1, char2: CHAR] RETURNS [ligatureOrKern: LigatureOrKern] = { RETURN[font.fontFormattingClass.ligKernProc[font, char1, char2]]; }; NSCode: PUBLIC PROCEDURE [font: FONT, char: CHAR] RETURNS [nsCode: INT] = { IF NOT font.fontGraphicsClass.containsProc[font, char] THEN RETURN[nonexistentNSCode] ELSE TRUSTED { fontFile: UFFileManager.FontFile _ UFFileManager.Open[font.nsCodeTable]; p: LONG POINTER TO CARDINAL _ fontFile.Pointer[] + LOOPHOLE[char, NAT]; IF NOT fontFile.InBounds[p] THEN RETURN[nonexistentNSCode]; RETURN[p^]; }; }; FindCharCode: PROCEDURE [font: FONT, nsCode: INT] RETURNS [char: CHAR] = TRUSTED { fontFile: UFFileManager.FontFile _ UFFileManager.Open[font.nsCodeTable]; code: CARDINAL _ nsCode; p: LONG POINTER TO CARDINAL _ fontFile.Pointer[]; IF NOT fontFile.InBounds[p, font.ec - '\000] THEN RETURN['\000]; IF (p+code)^ = code THEN RETURN['\000 + code]; FOR i: NAT IN [font.bc - '\000 .. font.ec - '\000] DO IF (p+i)^ = code THEN RETURN['\000 + i]; ENDLOOP; RETURN['\000]; }; GetWidthArray: PUBLIC PROCEDURE [font: FONT, minimum, undefined: FixedUnit] RETURNS [REF READONLY ARRAY CHAR OF FixedUnit] = { GetCache: ENTRY PROCEDURE = INLINE {IF font.cachedWidthsMinimum = minimum AND font.cachedWidthsUndefined = undefined THEN cachedWidths _ font.cachedWidths}; SetCache: ENTRY PROCEDURE = INLINE {font.cachedWidths _ cachedWidths; font.cachedWidthsMinimum _ minimum; font.cachedWidthsUndefined _ undefined}; cachedWidths: REF ARRAY CHAR OF FixedUnit _ NIL; GetCache[]; IF cachedWidths = NIL THEN { cachedWidths _ pZone.NEW[ARRAY CHAR OF FixedUnit]; FOR c: CHAR IN [FIRST[CHAR]..font.bc) DO cachedWidths[c] _ undefined ENDLOOP; FOR c: CHAR IN [font.bc..font.ec] DO cachedWidths[c] _ minimum.Max[Scaled.FromReal[Width[font, c]]] ENDLOOP; FOR c: CHAR IN (font.ec..LAST[CHAR]] DO cachedWidths[c] _ undefined ENDLOOP; SetCache[]; }; RETURN[cachedWidths]; }; AtomValuePair: TYPE = RECORD [atom: ATOM, value: REF ANY]; PutProp: PUBLIC ENTRY PROCEDURE [font: FONT, name: ATOM, value: REF ANY] = { list: LIST OF AtomValuePair _ NARROW[font.propertyList]; FOR a: LIST OF AtomValuePair _ list, a.rest UNTIL a=NIL DO IF a.first.atom = name THEN {a.first.value _ value; RETURN}; ENDLOOP; font.propertyList _ list _ CONS[[name, value], list]; }; GetProp: PUBLIC ENTRY PROCEDURE [font: FONT, name: ATOM] RETURNS [value: REF ANY] = { FOR a: LIST OF AtomValuePair _ NARROW[font.propertyList], a.rest UNTIL a=NIL DO IF a.first.atom = name THEN {value _ a.first.value; RETURN}; ENDLOOP; value _ NIL; }; Name: PUBLIC PROCEDURE [font: FONT] RETURNS [fontName: ROPE] = {RETURN[font.name]}; NextCharacterDirection: PUBLIC PROCEDURE [font: FONT] RETURNS [Direction] = {RETURN[right]}; NextLineDirection: PUBLIC PROCEDURE [font: FONT] RETURNS [Direction] = {RETURN[down]}; FormattingBox: PUBLIC PROCEDURE [font: FONT, char: CHAR] RETURNS [Box] = {RETURN[font.fontFormattingClass.formattingBoxProc[font, char]]}; FormattingMetric: PUBLIC PROCEDURE [font: FONT, metric: ATOM, char: CHAR _ '\000] RETURNS [REAL] = {RETURN[font.fontFormattingClass.formattingMetricProc[font, metric, char]]}; BoundingBox: PUBLIC PROCEDURE [font: FONT, char: CHAR] RETURNS [Box] = {RETURN[IF char IN [font.bc..font.ec] THEN font.fontGraphicsClass.boundingBoxProc[font, char] ELSE [Real.MinusZero, Real.MinusZero, Real.MinusZero, Real.MinusZero]]}; WidthVector: PUBLIC PROCEDURE [font: FONT, char: CHAR] RETURNS [Vec] = {RETURN[IF char IN [font.bc..font.ec] THEN font.fontGraphicsClass.widthVectorProc[font, char] ELSE [Real.MinusZero, Real.MinusZero]]}; ActualTransformation: PUBLIC PROCEDURE [font: FONT] RETURNS [Transformation] = {RETURN[font.actualTransformation]}; RequestedTransformation: PUBLIC PROCEDURE [font: FONT] RETURNS [Transformation] = {RETURN[font.requestedTransformation]}; Contains: PUBLIC PROCEDURE [font: FONT, char: CHAR] RETURNS [BOOLEAN] = {RETURN[font.fontGraphicsClass.containsProc[font, char]]}; CharCode: PUBLIC PROCEDURE [font: FONT, nsCode: INT] RETURNS [char: CHAR] = {RETURN[IF nsCode < 255 AND NSCode[font, '\000 + nsCode] = nsCode THEN '\000 + nsCode ELSE FindCharCode[font, nsCode]]}; DrawChar: PUBLIC PROCEDURE [font: FONT, char: CHAR, context: Context] = {IF char IN [font.bc..font.ec] THEN font.fontGraphicsClass.drawCharProc[font, char, context]}; Width: PUBLIC PROCEDURE [font: FONT, char: CHAR] RETURNS [REAL] = {box: Box _ FormattingBox[font, char]; RETURN[box.xmax-box.xmin]}; END.