DIRECTORY Atom USING [GetPName, GetProp, PutProp], Font, FS USING [StreamOpen], Imager USING [Error], ImagerTransform, IO USING [Close, PutF, real, rope, STREAM, time], Real USING [MinusZero, SqRt], RealFns USING [ArcTanDeg], RefText USING [TrustTextAsRope], Rope USING [Equal, Fetch, FromProc, Length, Map], Scaled USING [Float, FromReal, Max, PLUS, Round, Value, zero], UFFileManager, UFFontDirReader; UnifiedFontsImpl: CEDAR MONITOR IMPORTS Atom, FS, Imager, ImagerTransform, IO, Real, RealFns, RefText, Rope, Scaled, UFFileManager, UFFontDirReader EXPORTS Font = BEGIN OPEN Font; BadFontFileFormat: PUBLIC ERROR = CODE; rotationFuzz: REAL = 1/3600.0; fontDirFileName: ROPE _ "Cedar.FontDir"; fontHead: FONT _ NIL; -- all FONTs are linked together, starting here. defaultFont: FONT; defaultFontGenerated: BOOLEAN _ FALSE; DefaultFont: PROCEDURE [fontName: RopeOrRefText, transformation: Transformation, deviceType: ATOM] RETURNS [font: FONT] = { RememberFont: ENTRY PROC = INLINE {font.link _ fontHead; fontHead _ font}; IF defaultFontGenerated AND defaultFont = NIL THEN ERROR; IF defaultFont = NIL THEN {defaultFontGenerated _ TRUE; defaultFont _ Create["Xerox/PressFonts/Tioga/MRR", ImagerTransform.Scale[10], $Screen]}; font _ NEW[FontRec]; font^ _ defaultFont^; TRUSTED {font.name _ CopyRope[LOOPHOLE[fontName]]}; font.requestedTransformation _ transformation; font.deviceType _ deviceType; RememberFont[]; }; initProcRec: TYPE = RECORD [ init: PROC [font: FONT] ]; RegisterFontFormattingClass: PUBLIC PROC [flavor: ATOM, init: PROC [font: FONT]] = { Atom.PutProp[atom: flavor, prop: $FontFormattingClass, val: NEW[initProcRec _ [init: init]]]; }; RegisterFontGraphicsClass: PUBLIC PROCEDURE [flavor: ATOM, init: PROC [font: FONT]] = { Atom.PutProp[atom: flavor, prop: $FontGraphicsClass, val: 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]]; }; VecMag: PROC [x, y: REAL] RETURNS [REAL] = INLINE {RETURN[Real.SqRt[x*x + y*y]]}; Create: PUBLIC PROCEDURE [fontName: RopeOrRefText, transformation: Transformation, deviceType: ATOM] RETURNS [font: FONT] = { m: ImagerTransform.TransformationRec ~ transformation.Contents; name: ROPE _ IF ISTYPE[fontName, REF TEXT] THEN TrustAsRope[fontName] ELSE NARROW[fontName]; scale: REAL _ VecMag[m.a, m.d]; rotation: REAL _ RealFns.ArcTanDeg[m.d, m.a]; TestForMatch: UFFontDirReader.ExamineProc = { IF deviceType # candidateDeviceType THEN RETURN [FALSE]; IF scale < candidateMinSize OR scale > candidateMaxSize THEN RETURN [FALSE]; IF NOT name.Equal[RefText.TrustTextAsRope[candidateFontName], FALSE] THEN RETURN [FALSE]; IF candidateRotation >= 0 AND ABS[candidateRotation - 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 _ NEW[FontRec]; font.name _ CopyRope[name]; -- So it is OK to trust a REF TEXT as a ROPE font.requestedTransformation _ font.actualTransformation _ transformation; PutProp[font, $candidateSize, NEW[REAL _ candidateSize]]; font.deviceType _ candidateDeviceType; font.nsCodeTable _ codeScheme; font.formattingKey _ metricsName; font.graphicsKey _ graphicsName; formattingClassInit.init[font]; graphicsClassInit.init[font]; InitFontBoundingBox[font]; RememberFont[]; quit _ TRUE; }; LookAmongExistingFonts: ENTRY PROC RETURNS [FONT _ NIL] = { FOR f: FONT _ fontHead, f.link UNTIL f=NIL DO IF deviceType=f.deviceType AND transformation.Contents = f.requestedTransformation.Contents 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 { stream: IO.STREAM _ FS.StreamOpen["UnifiedFonts.errorLog", $append]; stream.PutF[ "%g Default substituted for %g font %g at %gbp, %g\n", IO.time[], IO.rope[Atom.GetPName[deviceType]], IO.rope[CopyRope[name]], IO.real[transformation.Contents.a -- *********************** --], IO.real[0 -- *********************** --] ]; stream.Close; Imager.Error[$NonexistentFont]; }; }; CreateScaled: PUBLIC PROCEDURE [fontName: RopeOrRefText, scale: REAL, deviceType: ATOM] RETURNS [font: FONT] = { RETURN[Create[fontName, ImagerTransform.Scale[scale], deviceType]] }; 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; }; 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: PUBLIC 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]; }; TextWidth: PUBLIC PROCEDURE [font: FONT, text: RopeOrRefText] RETURNS [REAL] ~ { w: Scaled.Value _ Scaled.zero; width: REF READONLY ARRAY CHAR OF FixedUnit _ GetWidthArray[font, Scaled.zero, Scaled.zero]; WITH text SELECT FROM t: REF TEXT => FOR i: NAT IN [0..t.length) DO w _ w.PLUS[width[t[i]]] ENDLOOP; r: ROPE => { action: PROC [c: CHAR] RETURNS [BOOLEAN] ~ { w _ w.PLUS[width[c]]; RETURN [FALSE] }; [] _ r.Map[action: action]; }; ENDCASE => w _ Scaled.zero; RETURN [Scaled.Float[w]] }; RoundedTextWidth: PUBLIC PROCEDURE [font: FONT, text: RopeOrRefText] RETURNS [INTEGER] ~ { w: Scaled.Value _ Scaled.zero; width: REF READONLY ARRAY CHAR OF FixedUnit _ GetWidthArray[font, Scaled.zero, Scaled.zero]; WITH text SELECT FROM t: REF TEXT => FOR i: NAT IN [0..t.length) DO w _ w.PLUS[width[t[i]]] ENDLOOP; r: ROPE => { action: PROC [c: CHAR] RETURNS [BOOLEAN] ~ { w _ w.PLUS[width[c]]; RETURN [FALSE] }; [] _ r.Map[action: action]; }; ENDCASE => w _ Scaled.zero; RETURN [Scaled.Round[w]] }; 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 _ 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 [Pair] = {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]]}; Width: PUBLIC PROCEDURE [font: FONT, char: CHAR] RETURNS [REAL] = {box: Box _ FormattingBox[font, char]; RETURN[box.xmax-box.xmin]}; END.  UnifiedFontsImpl.mesa Written November 1, 1982 4:53 pm Last edited by Michael Plass, February 17, 1984 1:07:01 pm PST Last edited by Doug Wyatt, November 22, 1983 4:46 pm the candidateSize is needed for strike fonts IF candidateSize # 0.0 THEN { font.actualTransformation _ ImagerTransform.Scale[candidateSize]; IF candidateRotation >= 0 THEN font.actualTransformation _ ImagerTransform.PreRotate[candidateRotation, font.actualTransformation]; }; Always want to return something. font _ DefaultFont[fontName, transformation, deviceType]; Κ ˜Jšœ™Jšœ ™ J™>J™4J˜šΟk ˜ Jšœœ˜(Jšœ˜Jšœœ˜Jšœœ ˜Jšœ˜Jšœœœ˜1Jšœœ˜Jšœœ ˜Jšœœ˜ Jšœœ'˜1Jšœœœ˜>Jšœ˜Jšœ˜—J˜Jšœœ˜JšœœœF˜sJšœ˜ Jšœœœ˜˜Iunitšœœœœ˜'Kšœœ ˜Kšœœ˜(Kšœ œœΟc0˜GKšœ œ˜Kšœœœ˜&š Οn œ œGœœœ˜{IcodešŸ œœœœ)˜JJš œœœœœ˜9JšœœœœZ˜Jšœœ ˜Jšœ˜Lšœœ ˜3Lšœ.˜.Lšœ˜Jšœ˜J˜—šœ œœ˜Lšœœœ˜L˜—š Ÿœœœ œœœ˜TLšœ<œ˜]Lšœ˜—š Ÿœœ œ œœœ˜WLšœ:œ˜[Lšœ˜—Kšœ œ ˜šŸœœ œœ˜,Jšœ˜Jšœ˜Jšœœœ˜Jšœ˜—š Ÿœ œœœœ˜3Jšœœ˜ Jš Ÿœ œœœœ˜AJšœ1˜7Jšœ˜—š Ÿ œ œœœœœ˜=Jšœœ˜Jšœ˜—KšŸœœœœœœœ˜Qš Ÿœœ œGœœœ˜}Lšœ?˜?Lšœœœœ œœœœœ ˜\Lšœœ˜Lšœ œ˜-šŸ œ!˜-Lšœ"œœœ˜8Lš œœœœœ˜LLš œœ8œœœœ˜YLš œœœ/œœœ˜dLšœœ˜L˜—LšŸ œœœ+˜CšŸ œ ˜*Lšœœœ>˜kLšœœœ=˜hLšœœ ˜Lšœž,˜HLšœJ˜Jšœœœ˜9Lšœ,™,—šœœ™JšœA™AJšœœe™ƒJšœ™—Lšœ&˜&Lšœ˜Lšœ!˜!Lšœ ˜ Lšœ˜Lšœ˜L˜Lšœ˜Lšœœ˜ L˜—š Ÿœœœœœœ˜;š œœœœ˜-Jš œœ>œœœœ˜‰Jšœ˜—J˜—Lšœ ˜ LšœœœS˜cšœœ˜J™ Jšœœœœ.˜Dšœ ˜ Jšœ7˜7Jšœ˜ Jšœ!˜#Jšœ˜Jšœ?˜AJšœ&˜(J˜—Jšœ ˜ J˜Jšœ9™9Jšœ˜—L˜—šŸ œœ œ"œœœœ˜pJšœ<˜BJ˜—šŸœ œœ˜/J˜šœœœ˜$Jšœ ˜ Jšœ œ˜!Jšœ œ˜!Jšœ œ˜!Jšœ œ˜!Jšœ˜—Jšœ˜Jšœ˜—šŸœœœœ!˜aL˜—š Ÿœœ œœœœ%˜qLšœ;˜AL˜—šŸœœ œœœœ œ˜KLšœœ1œœ˜Ušœœ˜JšœH˜HJš œœœœœœœ˜GJšœœœœ˜;Jšœ˜ Jšœ˜—Lšœ˜—šŸ œœ œœ œœœœ˜YJšœH˜HJšœœ ˜Jš œœœœœ˜1Jšœœ'œœ˜@Jšœœœ˜.šœœœ&˜5Jšœœœ ˜(Jšœ˜—Jšœ˜L˜—š Ÿ œœ œœœœ˜PJ˜Jš œœœœœœ;˜\šœœ˜Jšœœœœœœœœœ˜Nšœœ˜ š œœœœœ˜,Jšœœ ˜Jšœœ˜Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—š Ÿœœ œœœœ˜ZJ˜Jš œœœœœœ;˜\šœœ˜Jšœœœœœœœœœ˜Nšœœ˜ š œœœœœ˜,Jšœœ ˜Jšœœ˜Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—šŸ œœ œœ!œœœœœœ˜~JšŸœœ œœœ$œ(œ#˜œJšŸœœ œœp˜’Jš œœœœœ œ˜0J˜ šœœœ˜Jš œœœœœ ˜,Jšœœœœœ œœ˜Mšœœœ˜$Jšœ>˜>Jšœ˜—Jšœœœ œœœœ˜LJ˜ Jšœ˜—Jšœ˜Jšœ˜—Kš œœœœ œœ˜:šŸœœœ œœœ œœ˜LJšœœœœ˜8š œœœœœ˜:Jšœœœ˜