UnifiedFontsImpl.mesa
Written November 1, 1982 4:53 pm
Last edited by Michael Plass, February 25, 1983 1:19 pm
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: FONTNIL; -- 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]<Tioga>Strikefonts>Tioga10.strike"];
formattingClassInit.init[font];
graphicsClassInit.init[font];
InitFontBoundingBox[font];
font.bitmapCacheable ← Area[font.fontBoundingBox]<areaLimit;
defaultFont ← font;
};
initProcRec: TYPE = RECORD [
init: PROC [font: FONT]
];
RegisterFontFormattingClass: PUBLIC PROC [flavor: ATOM, init: PROC [font: FONT]] = {
Atom.PutProp[atom: flavor, prop: $FontFormattingClass, val: pZone.NEW[initProcRec ← [init: init]]];
};
RegisterFontGraphicsClass: PUBLIC PROCEDURE [flavor: ATOM, init: PROC [font: FONT]] = {
Atom.PutProp[atom: flavor, prop: $FontGraphicsClass, val: pZone.NEW[initProcRec ← [init: init]]];
};
areaLimit: REAL ← 80*80;
Area: PROC [b: Box] RETURNS [area: REAL] = {
OPEN b;
area ← (ymax-ymin)*(xmax-xmin);
IF area<0 THEN ERROR;
};
CopyRope: PROCEDURE [rope: ROPE] RETURNS [ROPE] = {
i: INT ← -1;
CharProc: PROCEDURE RETURNS [CHAR] = {RETURN[rope.Fetch[i←i+1]]};
RETURN[Rope.FromProc[len: rope.Length[], p: CharProc]];
};
TrustAsRope: PROCEDURE [text: REF] RETURNS [ROPE] = TRUSTED {
RETURN[LOOPHOLE[text]];
};
Create: PUBLIC PROCEDURE [fontName: RopeOrRefText, transformation: Transformation, deviceType: DeviceType] RETURNS [font: FONT] = {
name: ROPEIF ISTYPE[fontName, REF TEXT] THEN TrustAsRope[fontName] ELSE NARROW[fontName];
TestForMatch: UFFontDirReader.ExamineProc = {
IF deviceType # candidateDeviceType THEN RETURN [FALSE];
IF transformation.scale < candidateMinSize OR transformation.scale > 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]<areaLimit;
RememberFont[];
quit ← TRUE;
};
LookAmongExistingFonts: ENTRY PROC RETURNS [FONTNIL] = {
FOR f: FONT ← fontHead, f.link UNTIL f=NIL DO
IF deviceType=f.deviceType AND transformation = f.requestedTransformation AND name.Equal[f.name, FALSE] THEN RETURN[f];
ENDLOOP;
};
font ← LookAmongExistingFonts[];
IF font=NIL THEN UFFontDirReader.EnumerateFontDirectory[fontDirFileName, TestForMatch, SaveResult];
IF font=NIL THEN {
Always want to return something.
msg: ROPEIO.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: ROPEIF 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𡤀
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.