DIRECTORY Atom USING [GetPName], Basics USING [CompareCard, Comparison, LongNumber], CardTab USING [Create, EachPairAction, Fetch, Insert, Pairs, Ref, Store, Update, UpdateAction], ImagerBox USING [Extents], ImagerFDBuild USING [CharacterDescription, CharacterDescriptionCell, CharacterDescriptionRep, CharacterDescriptions, FDTable, FDTableRep], ImagerFDTypeface USING [CharInfo, CharInfoRun, CharInfoRunRep, CharInfoRunSeq, CharInfoRunSeqRep, CharRunEntry, CharRuns, CharRunsRep, CharSet, Data, DataRep, FromStream, OverflowCharInfoSeq, OverflowCharInfoSeqRep, OverflowIndex, password, RealCode, RealSeq, RealSeqRep, TransformationSeq, TransformationSeqRep, TypefaceCell, TypefaceCellSeq, TypefaceCellSeqRep, TypefaceIndex, VerboseCharInfo, VerboseCharInfoRep], ImagerSys USING [OpenInputFile, OpenOutputFile], ImagerTransformation USING [Equal, Factor, FactoredTransformation, Scale, Transformation], IO USING [Close, PutByte, PutRope, STREAM], List USING [CompareProc, Length, Sort], Prop USING [PropList], Real USING [Fix, FScale, Round], RefTab USING [Create, EachPairAction, EqualProc, Fetch, GetSize, HashProc, Insert, Pairs, Ref, Update, UpdateAction], Rope USING [ROPE, Size], SymTab USING [Create, Ref, Update, UpdateAction], Vector2 USING [VEC]; ImagerFDBuildImpl: CEDAR PROGRAM IMPORTS ImagerFDTypeface, Atom, Basics, CardTab, ImagerTransformation, ImagerSys, IO, SymTab, List, Real, Rope, RefTab EXPORTS ImagerFDBuild ~ BEGIN OPEN ImagerFDBuild, ImagerFDTypeface; ROPE: TYPE ~ Rope.ROPE; Transformation: TYPE ~ ImagerTransformation.Transformation; Conflict: PUBLIC SIGNAL [charCode: CARD, cds: CharacterDescriptions] RETURNS [CharacterDescription] = CODE; CompareReal: PROC [a, b: REAL] RETURNS [Basics.Comparison] = { RETURN [SELECT TRUE FROM a < b => less, a = b => equal, ENDCASE => greater] }; extentTolerance: REAL ¬ 0.02; -- error allowed for extents (fraction of em) escapementTolerance: REAL ¬ 1.0/10000.0; -- error allowed for escapements (fraction of em) Create: PUBLIC PROC RETURNS [FDTable] = { RETURN [NEW[FDTableRep ¬ [cardTab: CardTab.Create[]]]] }; Insert: PUBLIC PROC [fdTable: FDTable, code: CARD, cd: CharacterDescriptionRep] = { Inner: CardTab.UpdateAction = { newCDs: CharacterDescriptions = NEW[CharacterDescriptionCell ¬ [first: NEW[CharacterDescriptionRep ¬ cd], rest: NARROW[val]]]; RETURN [store, newCDs] }; CardTab.Update[x: fdTable.cardTab, key: code, action: Inner]; fdTable.charToDeviceTransformations ¬ NIL; }; FetchList: PUBLIC PROC [fdTable: FDTable, code: CARD] RETURNS [CharacterDescriptions] = { RETURN [NARROW[CardTab.Fetch[fdTable.cardTab, code].val]] }; StoreList: PUBLIC PROC [fdTable: FDTable, code: CARD, cds: CharacterDescriptions] = { [] ¬ CardTab.Store[x: fdTable.cardTab, key: code, val: cds]; fdTable.charToDeviceTransformations ¬ NIL; }; RealTab: TYPE = CardTab.Ref; RealAssoc: TYPE = RECORD [real: REAL, card: CARD, subsume: REF RealAssoc ¬ NIL]; RealAssocCompare: List.CompareProc = { a: REF RealAssoc = NARROW[ref1]; b: REF RealAssoc = NARROW[ref2]; RETURN [Basics.CompareCard[b.card, a.card]] -- sort in decreasing order by cardinal. }; CountReal: PROC [realTab: RealTab, real: REAL] = { Inner: CardTab.UpdateAction = { IF found THEN { c: REF RealAssoc = NARROW[val]; c.card ¬ c.card + 1 } ELSE { RETURN [op: store, new: NEW [RealAssoc ¬ [real: real, card: 1]]] }; }; CardTab.Update[x: realTab, key: LOOPHOLE[real], action: Inner]; }; CommonReals: TYPE = REF CommonRealsRep; CommonRealsRep: TYPE = RECORD [ realToIndex: RealTab, realForCode: RealSeq ]; FindCommonReals: PROC [realTab: RealTab, tolerance: REAL ¬ 0.0, max: RealCode ¬ 255] RETURNS [CommonReals] = { list: LIST OF REF ANY ¬ NIL; ans: CommonReals ¬ NEW[CommonRealsRep]; EachEntry: CardTab.EachPairAction = { list ¬ CONS[val, list]; }; [] ¬ CardTab.Pairs[x: realTab, action: EachEntry]; IF tolerance # 0.0 THEN { new: LIST OF REF ANY ¬ NIL; clusterStart: REF RealAssoc ¬ NIL; RealAssocOrderCompare: List.CompareProc = { a: REF RealAssoc = NARROW[ref1]; b: REF RealAssoc = NARROW[ref2]; RETURN [CompareReal[b.real, a.real]] -- sort by decreasing order }; CloseEnough: PROC [a, b: REAL] RETURNS [BOOL] = { IF (a>0) # (b>0) THEN RETURN [FALSE]; RETURN [ABS[a-b] <= tolerance] }; list ¬ List.Sort[list: list, compareProc: RealAssocOrderCompare]; FOR tail: LIST OF REF ANY ¬ list, tail.rest UNTIL tail = NIL DO this: REF RealAssoc = NARROW[tail.first]; IF clusterStart = NIL THEN {clusterStart ¬ this} ELSE { IF CloseEnough[this.real, clusterStart.real] THEN { this.subsume ¬ clusterStart.subsume; clusterStart.subsume ¬ this; clusterStart.card ¬ clusterStart.card + this.card; } ELSE { new ¬ CONS[clusterStart, new]; clusterStart ¬ this; }; }; ENDLOOP; IF clusterStart # NIL THEN {new ¬ CONS[clusterStart, new]; clusterStart ¬ NIL }; list ¬ new; }; list ¬ List.Sort[list: list, compareProc: RealAssocCompare]; ans.realToIndex ¬ CardTab.Create[]; ans.realForCode ¬ NEW[RealSeqRep[MIN[List.Length[list], max+1]]]; FOR i: RealCode IN [0..ans.realForCode.size) DO r: REF RealAssoc = NARROW[list.first]; ans.realForCode[i] ¬ r.real; FOR a: REF RealAssoc ¬ r, a.subsume UNTIL a = NIL DO IF NOT CardTab.Insert[x: ans.realToIndex, key: LOOPHOLE[a.real], val: NEW[RealAssoc ¬ [real: r.real, card: i]]] THEN ERROR; ENDLOOP; list ¬ list.rest; ENDLOOP; RETURN [ans] }; GetRealCode: PROC [commonReals: CommonReals, real: REAL] RETURNS [INT] = { found: BOOL; val: REF; [found, val] ¬ CardTab.Fetch[x: commonReals.realToIndex, key: LOOPHOLE[real]]; RETURN [IF found THEN INT[NARROW[val, REF RealAssoc].card] ELSE -1] }; SetArrayTable: TYPE = REF SetArrayTableRep; SetArrayTableRep: TYPE = ARRAY CharSet OF REF ARRAY BYTE OF CharacterDescriptions ¬ ALL[NIL]; BuildSetArrayTable: PROC [fdTable: FDTable] RETURNS [SetArrayTable] = { setArrayTable: SetArrayTable ¬ NEW[SetArrayTableRep]; EachChar: CardTab.EachPairAction = { set: CharSet = key / 256; code: BYTE = key MOD 256; IF setArrayTable[set] = NIL THEN setArrayTable[set] ¬ NEW[ARRAY BYTE OF CharacterDescriptions ¬ ALL[NIL]]; IF setArrayTable[set][code] # NIL THEN ERROR; setArrayTable[set][code] ¬ NARROW[val]; }; [] ¬ CardTab.Pairs[x: fdTable.cardTab, action: EachChar]; RETURN [setArrayTable]; }; Compatible: PROC [a, b: CharacterDescriptions] RETURNS [BOOL] = { UNTIL (a = NIL) OR (b = NIL) OR (a = b) DO ac: CharacterDescription = a.first; bc: CharacterDescription = b.first; IF a = b THEN RETURN [TRUE]; IF NOT ( ac.typefaceCell=bc.typefaceCell AND ac.charToDevice=bc.charToDevice AND ac.xcCodeDelta=bc.xcCodeDelta AND ac.bits=bc.bits AND ac.placement=bc.placement ) THEN RETURN [FALSE]; a ¬ a.rest; b ¬ b.rest; ENDLOOP; RETURN [a = b] }; RunProc: TYPE = PROC [start, end: CARD, constant: BOOL]; ForEachRun: PROC [setArrayTable: SetArrayTable, runProc: RunProc] = { prevCDS: CharacterDescriptions ¬ NIL; startCode: CARD ¬ CARD.LAST; finalCode: CARD ¬ 0; constant: BOOL ¬ FALSE; FOR set: CharSet IN CharSet DO IF setArrayTable[set] # NIL THEN { t: REF ARRAY BYTE OF CharacterDescriptions ¬ setArrayTable[set]; FOR code: BYTE IN BYTE DO cds: CharacterDescriptions ¬ t[code]; IF NOT Compatible[cds, prevCDS] THEN { IF startCode <= finalCode THEN { runProc[startCode, finalCode.SUCC, constant]; startCode ¬ CARD.LAST; finalCode ¬ 0; prevCDS ¬ NIL; constant ¬ TRUE; }; }; IF cds # NIL THEN { card: CARD = set*256+code; IF prevCDS # NIL AND prevCDS # cds THEN constant ¬ FALSE; prevCDS ¬ cds; startCode ¬ MIN[startCode, card]; finalCode ¬ MAX[finalCode, card]; }; ENDLOOP; IF startCode <= finalCode THEN { runProc[startCode, finalCode.SUCC, constant]; startCode ¬ CARD.LAST; finalCode ¬ 0; }; }; ENDLOOP; }; CommonItems: TYPE = REF CommonItemsRep; CommonItemsRep: TYPE = RECORD [ leftExtents: CommonReals, rightExtents: CommonReals, descents: CommonReals, ascents: CommonReals, escapements: CommonReals, placements: FactoredSeq, placementTab: RefTab.Ref, typefaces: TypefaceCellSeq, typefaceTab: RefTab.Ref ]; CommonItemsFromSetArrayTable: PROC [setArrayTable: SetArrayTable] RETURNS [CommonItems] = { leftExtentCount: RealTab = CardTab.Create[]; rightExtentCount: RealTab = CardTab.Create[]; descentCount: RealTab = CardTab.Create[]; ascentCount: RealTab = CardTab.Create[]; escapementCount: RealTab = CardTab.Create[]; typefaceTab: RefTab.Ref = RefTab.Create[]; placementTab: RefTab.Ref = RefTab.Create[equal: TransformationEqual, hash: TransformationHash]; result: CommonItems = NEW[CommonItemsRep]; nTypefaceCells: NAT ¬ 0; UpdateTypefaceTab: RefTab.UpdateAction = { IF NOT found THEN { new ¬ NEW[INT ¬ nTypefaceCells]; op ¬ store; nTypefaceCells ¬ nTypefaceCells + 1; }; }; EachRun: PROC [start, end: CARD, constant: BOOL] = { set: CharSet = start / 256; a: REF ARRAY BYTE OF CharacterDescriptions = setArrayTable[set]; base: CARD = set * 256; IF constant THEN end ¬ start+1; FOR c: CARD IN [start..end) DO FOR tail: CharacterDescriptions ¬ a[c - base], tail.rest UNTIL tail = NIL DO cd: CharacterDescription = tail.first; placement: Transformation = NonNilT[cd.placement]; RefTab.Update[x: typefaceTab, key: cd.typefaceCell, action: UpdateTypefaceTab]; [] ¬ RefTab.Insert[x: placementTab, key: placement, val: placement]; CountReal[leftExtentCount, cd.extents.leftExtent]; CountReal[rightExtentCount, cd.extents.rightExtent]; CountReal[descentCount, cd.extents.descent]; CountReal[ascentCount, cd.extents.ascent]; CountReal[escapementCount, cd.escapement.x]; CountReal[escapementCount, cd.escapement.y]; ENDLOOP; ENDLOOP; }; StuffTypefaceIndex: RefTab.EachPairAction = { index: REF INT = NARROW[val]; result.typefaces[index­] ¬ NARROW[key]; }; ForEachRun[setArrayTable, EachRun]; result.leftExtents ¬ FindCommonReals[leftExtentCount, extentTolerance, 254]; result.rightExtents ¬ FindCommonReals[rightExtentCount, extentTolerance]; result.descents ¬ FindCommonReals[descentCount, extentTolerance]; result.ascents ¬ FindCommonReals[ascentCount, extentTolerance]; result.escapements ¬ FindCommonReals[escapementCount, escapementTolerance]; result.typefaces ¬ NEW[TypefaceCellSeqRep[nTypefaceCells]]; [] ¬ RefTab.Pairs[x: typefaceTab, action: StuffTypefaceIndex]; result.typefaceTab ¬ typefaceTab; result.placements ¬ SortTransformations[placementTab]; result.placementTab ¬ RefTab.Create[equal: TransformationEqual, hash: TransformationHash]; FOR i: NAT IN [0..result.placements.size) DO [] ¬ RefTab.Insert[x: result.placementTab, key: result.placements[i].m, val: NEW[INT ¬ i]]; ENDLOOP; RETURN [result] }; GetTypefaceIndex: PROC [common: CommonItems, typefaceCell: TypefaceCell] RETURNS [TypefaceIndex] = { i: REF INT = NARROW[RefTab.Fetch[common.typefaceTab, typefaceCell].val]; RETURN [i­] }; GetPlacementCode: PROC [common: CommonItems, m: Transformation] RETURNS [CARDINAL] = { i: REF INT = NARROW[RefTab.Fetch[common.placementTab, NonNilT[m]].val]; RETURN [i­]; }; FDTypefaceDataFromCanonicalizedFDTable: PUBLIC PROC [fdTable: FDTable] RETURNS [data: Data] = { nt: NAT = fdTable.charToDeviceTransformations.size; setArrayTable: SetArrayTable = BuildSetArrayTable[fdTable]; common: CommonItems = CommonItemsFromSetArrayTable[setArrayTable]; lastRuns: CharRuns ¬ NIL; charInfoRunTab: RefTab.Ref = RefTab.Create[equal: CharInfoRunEqual, hash: CharInfoRunHash]; nCharInfoRuns: INT ¬ 0; StuffCharInfoRunSeq: RefTab.EachPairAction = { charInfoRun: CharInfoRun = NARROW[key]; refInt: REF INT = NARROW[val]; data.charInfoRunSeq[refInt­] ¬ charInfoRun; }; EachRun: PROC [start, end: CARD, constant: BOOL] = { set: CharSet = start / 256; a: REF ARRAY BYTE OF CharacterDescriptions = setArrayTable[set]; base: CARD = set * 256; charRunCell: CharRuns = NEW[CharRunsRep[nt]]; charInfoRunSeq: CharInfoRunSeq = NEW[CharInfoRunSeqRep[nt]]; -- scratch storage charRunCell.bc ¬ start-base; charRunCell.ec ¬ end-1-base; IF data.setTable[set] = NIL THEN data.setTable[set] ¬ lastRuns ¬ charRunCell ELSE lastRuns ¬ lastRuns.link ¬ charRunCell; -- append to end of run list IF constant THEN { i: NAT ¬ 0; FOR tail: CharacterDescriptions ¬ a[start - base], tail.rest UNTIL tail = NIL DO cd: CharacterDescription = tail.first; charInfoRun: REF CharInfoRunRep ¬ NEW[CharInfoRunRep[1]]; UNTIL cd.charToDevice = fdTable.charToDeviceTransformations[i] DO i ¬ i + 1 ENDLOOP; charRunCell[i].typefaceIndex ¬ GetTypefaceIndex[common, cd.typefaceCell]; charInfoRunSeq[i] ¬ charInfoRun; charInfoRun.xcCodeDelta ¬ cd.xcCodeDelta; charInfoRun.bits ¬ cd.bits; charInfoRun.placementCode ¬ GetPlacementCode[common, cd.placement]; TRUSTED {charInfoRun[0] ¬ CharInfoFromCD[cd]}; ENDLOOP; } ELSE { FOR c: CARD IN [start..end) DO i: NAT ¬ 0; FOR tail: CharacterDescriptions ¬ a[c - base], tail.rest UNTIL tail = NIL DO cd: CharacterDescription = tail.first; charInfoSeq: REF CharInfoRunRep ¬ NIL; UNTIL cd.charToDevice = fdTable.charToDeviceTransformations[i] DO i ¬ i + 1 ENDLOOP; IF charInfoRunSeq[i] = NIL THEN { charRunCell[i].typefaceIndex ¬ GetTypefaceIndex[common, cd.typefaceCell]; charInfoRunSeq[i] ¬ charInfoSeq ¬ NEW[CharInfoRunRep[end-start]]; charInfoSeq.xcCodeDelta ¬ cd.xcCodeDelta; charInfoSeq.bits ¬ cd.bits; charInfoSeq.placementCode ¬ GetPlacementCode[common, cd.placement]; } ELSE charInfoSeq ¬ charInfoRunSeq[i]; TRUSTED { charInfoSeq[c - start] ¬ CharInfoFromCD[cd] }; ENDLOOP; ENDLOOP; }; FOR i: NAT IN [0..nt) DO IF charInfoRunSeq[i] # NIL THEN { NoteCharInfoRun: RefTab.UpdateAction = { IF NOT found THEN { op ¬ store; new ¬ NEW[INT ¬ nCharInfoRuns]; nCharInfoRuns ¬ nCharInfoRuns + 1; }; }; RefTab.Update[x: charInfoRunTab, key: charInfoRunSeq[i], action: NoteCharInfoRun]; }; ENDLOOP; FOR i: NAT IN [0..nt) DO IF charInfoRunSeq[i] # NIL THEN { charRunCell[0].typefaceIndex ¬ charRunCell[i].typefaceIndex; charInfoRunSeq[0] ¬ charInfoRunSeq[i]; EXIT; }; ENDLOOP; IF charInfoRunSeq[0] = NIL THEN ERROR; -- oops, this should not have been a run. FOR i: NAT IN [0..nt) DO IF charInfoRunSeq[i] = NIL THEN { charRunCell[i].typefaceIndex ¬ charRunCell[0].typefaceIndex; charInfoRunSeq[i] ¬ charInfoRunSeq[0]; }; ENDLOOP; FOR i: NAT IN [0..nt) DO refInt: REF INT = NARROW[RefTab.Fetch[charInfoRunTab, charInfoRunSeq[i]].val]; charRunCell[i].charInfoRunIndex ¬ refInt­; ENDLOOP; }; overflowList: LIST OF VerboseCharInfo ¬ NIL; nOver: NAT ¬ 0; CharInfoFromCD: PROC [cd: CharacterDescription] RETURNS [CharInfo] = { codeLeftExtent: INT = GetRealCode[common.leftExtents, cd.extents.leftExtent]; codeRightExtent: INT = GetRealCode[common.rightExtents, cd.extents.rightExtent]; codeDescent: INT = GetRealCode[common.descents, cd.extents.descent]; codeAscent: INT = GetRealCode[common.ascents, cd.extents.ascent]; codeEscapementX: INT = GetRealCode[common.escapements, cd.escapement.x]; codeEscapementY: INT = GetRealCode[common.escapements, cd.escapement.y]; data.fontExtents.leftExtent ¬ MAX[data.fontExtents.leftExtent, cd.extents.leftExtent]; data.fontExtents.rightExtent ¬ MAX[data.fontExtents.rightExtent, cd.extents.rightExtent]; data.fontExtents.descent ¬ MAX[data.fontExtents.descent, cd.extents.descent]; data.fontExtents.ascent ¬ MAX[data.fontExtents.ascent, cd.extents.ascent]; IF MIN[codeEscapementY, codeEscapementX, codeLeftExtent, codeRightExtent, codeDescent, codeAscent] >= 0 AND cd.propList = NIL THEN { RETURN [[short[ leftExtent: codeLeftExtent, rightExtent: codeRightExtent, descent: codeDescent, ascent: codeAscent, escapementX: codeEscapementX, escapementY: codeEscapementY]]] } ELSE { overflowIndex: OverflowIndex = nOver; vc: VerboseCharInfo = NEW[VerboseCharInfoRep ¬ [ extents: cd.extents, escapement: cd.escapement, propList: cd.propList ]]; overflowList ¬ CONS[vc, overflowList]; nOver ¬ nOver+1; RETURN [[verbose[overflowIndex]]] }; }; data ¬ NEW[DataRep]; data.fontExtents ¬ [-10000.0, -10000.0, -10000.0, -10000.0]; ForEachRun[setArrayTable, EachRun]; data.charInfoRunSeq ¬ NEW[CharInfoRunSeqRep[nCharInfoRuns]]; [] ¬ RefTab.Pairs[x: charInfoRunTab, action: StuffCharInfoRunSeq]; data.charToDeviceTransformations ¬ fdTable.charToDeviceTransformations; data.commonLeftExtents ¬ common.leftExtents.realForCode; data.commonRightExtents ¬ common.rightExtents.realForCode; data.commonDescents ¬ common.descents.realForCode; data.commonAscents ¬ common.ascents.realForCode; data.commonEscapements ¬ common.escapements.realForCode; data.typefaceCellSeq ¬ common.typefaces; data.placementTransformations ¬ NEW[TransformationSeqRep[common.placements.size]]; FOR i: NAT IN [0..common.placements.size) DO data.placementTransformations[i] ¬ common.placements[i].m; ENDLOOP; data.propList ¬ fdTable.propList; data.overflowCharInfoSeq ¬ NEW[OverflowCharInfoSeqRep[nOver]]; FOR i: OverflowIndex DECREASING IN [0..nOver) DO data.overflowCharInfoSeq[i] ¬ overflowList.first; overflowList ¬ overflowList.rest; ENDLOOP; IF data.fontExtents.ascent < -data.fontExtents.descent THEN data.fontExtents ¬ [0.0, 0.0, 0.0, 0.0]; }; RawHash: UNSAFE PROC [p: LONG POINTER TO CARDINAL, bytes: INT] RETURNS [hash: CARDINAL ¬ 31415] = UNCHECKED { IF p # NIL THEN WHILE (bytes ¬ bytes - BYTES[WORD]) >= 0 DO hash ¬ hash + hash*128 + p­; p ¬ p + SIZE[WORD]; ENDLOOP; }; CharacterDescriptionHash: RefTab.HashProc = TRUSTED { a: CharacterDescription = NARROW[key]; hash: CARDINAL = RawHash[LOOPHOLE[a], BYTES[CharacterDescriptionRep]]; RETURN [hash] }; CharacterDescriptionEqual: RefTab.EqualProc = { a1: CharacterDescription = NARROW[key1]; a2: CharacterDescription = NARROW[key2]; RETURN [a1­ = a2­] }; CharacterDescriptionsHash: RefTab.HashProc = TRUSTED { c: CharacterDescriptions = NARROW[key]; a: ARRAY [0..2) OF REF ¬ [c.first, c.rest]; p: LONG POINTER TO ARRAY [0..2) OF REF = @a; hash: CARDINAL = RawHash[LOOPHOLE[p], BYTES[ARRAY [0..2) OF REF]]; RETURN [hash] }; CharacterDescriptionsEqual: RefTab.EqualProc = { a1: CharacterDescriptions = NARROW[key1]; a2: CharacterDescriptions = NARROW[key2]; RETURN [a1­ = a2­] }; TransformationHash: RefTab.HashProc = TRUSTED { m: Transformation = NARROW[key]; IF m = NIL THEN RETURN [13] ELSE { a: ARRAY [0..6) OF REAL ¬ [ABS[m.a], ABS[m.b], ABS[m.c], ABS[m.d], ABS[m.e], ABS[m.f]]; p: LONG POINTER TO ARRAY [0..6) OF REAL = @a; hash: CARDINAL = RawHash[LOOPHOLE[p], BYTES[ARRAY [0..6) OF REAL]]; RETURN [hash] }; }; TransformationEqual: RefTab.EqualProc = { a1: Transformation = NARROW[key1]; a2: Transformation = NARROW[key2]; RETURN [(a1=NIL AND a2=NIL) OR ImagerTransformation.Equal[a1, a2]] }; CharInfoRunHash: RefTab.HashProc = TRUSTED { charInfoRun: CharInfoRun = NARROW[key]; hash: CARDINAL = RawHash[LOOPHOLE[charInfoRun], BYTES[CharInfoRunRep[charInfoRun.size]]]; RETURN [hash] }; CharInfoRunEqual: RefTab.EqualProc = TRUSTED { a1: CharInfoRun = NARROW[key1]; a2: CharInfoRun = NARROW[key2]; IF NOT ( a1.size = a2.size AND a1.xcCodeDelta = a2.xcCodeDelta AND a1.bits = a2.bits AND a1.placementCode = a2.placementCode ) THEN RETURN [FALSE]; FOR i: NAT IN [0..a1.size) DO c1: CharInfo = a1[i]; c2: CharInfo = a2[i]; WITH c1: c1 SELECT FROM short => { WITH c2: c2 SELECT FROM short => { IF c1 # c2 THEN RETURN [FALSE]; }; ENDCASE => RETURN [FALSE]; }; verbose => { WITH c2: c2 SELECT FROM verbose => { IF c1 # c2 THEN RETURN [FALSE]; }; ENDCASE => RETURN [FALSE]; }; ENDCASE => ERROR; ENDLOOP; RETURN [TRUE]; }; MapCharacterDescriptions: PROC [fdTable: FDTable, proc: PROC [cd: CharacterDescription] RETURNS [CharacterDescription]] = { EachPair: CardTab.EachPairAction = { FOR tail: CharacterDescriptions ¬ NARROW[val], tail.rest UNTIL tail = NIL DO tail.first ¬ proc[tail.first]; ENDLOOP; }; [] ¬ CardTab.Pairs[x: fdTable.cardTab, action: EachPair]; }; identity: Transformation = ImagerTransformation.Scale[1]; NonNilT: PROC [m: Transformation] RETURNS [Transformation] = { RETURN [IF m = NIL THEN identity ELSE m] }; Canonicalize: PUBLIC PROC [fdTable: FDTable] = { mTab: RefTab.Ref = RefTab.Create[equal: TransformationEqual, hash: TransformationHash]; charToDeviceTab: RefTab.Ref = RefTab.Create[]; cdTab: RefTab.Ref = RefTab.Create[equal: CharacterDescriptionEqual, hash: CharacterDescriptionHash]; tfTab: SymTab.Ref = SymTab.Create[case: FALSE]; CanonicalizeTypefaceCell: PROC [typefaceCell: TypefaceCell] RETURNS [TypefaceCell] = { UpdateTF: SymTab.UpdateAction = { IF NOT found THEN RETURN [store, typefaceCell] ELSE { canon: TypefaceCell = NARROW[val]; IF canon = typefaceCell THEN RETURN; IF canon.typeface = NIL AND typefaceCell.typeface # NIL THEN { canon­ ¬ typefaceCell­; }; typefaceCell ¬ canon; }; }; IF typefaceCell.typeface # NIL THEN { typefaceCell.fontName ¬ typefaceCell.typeface.name; typefaceCell.createDate ¬ typefaceCell.typeface.created; typefaceCell.type ¬ typefaceCell.typeface.class.type; }; IF typefaceCell.fontName = NIL THEN ERROR; -- can't do anonymous fonts SymTab.Update[x: tfTab, key: typefaceCell.fontName, action: UpdateTF]; RETURN [typefaceCell]; }; CanonicalizeTransformation: PROC [m: Transformation] RETURNS [Transformation] = { UpdateTransformation: RefTab.UpdateAction = { IF found THEN m ¬ NARROW[val] ELSE RETURN [store, m] }; m ¬ NonNilT[m]; RefTab.Update[x: mTab, key: m, action: UpdateTransformation]; RETURN [m] }; CanonicalizeCharacterDescription: PROC [cd: CharacterDescription] RETURNS [CharacterDescription] = { UpdateCD: RefTab.UpdateAction = { IF found THEN { cd ¬ NARROW[val] } ELSE RETURN [store, cd]; }; cd.charToDevice ¬ CanonicalizeTransformation[cd.charToDevice]; [] ¬ RefTab.Insert[charToDeviceTab, cd.charToDevice, cd.charToDevice]; cd.typefaceCell ¬ CanonicalizeTypefaceCell[cd.typefaceCell]; cd.placement ¬ CanonicalizeTransformation[IF cd.placement = NIL THEN identity ELSE cd.placement]; RefTab.Update[x: cdTab, key: cd, action: UpdateCD]; RETURN [cd]; }; cdsTab: RefTab.Ref = RefTab.Create[equal: CharacterDescriptionsEqual, hash: CharacterDescriptionsHash]; CanonicalizeCharacterDescriptions: PROC [cds: CharacterDescriptions] RETURNS [CharacterDescriptions] = { UpdateCDS: RefTab.UpdateAction = { IF found THEN cds ¬ NARROW[val] ELSE RETURN [store, cds] }; IF cds = NIL THEN RETURN [NIL]; cds.rest ¬ CanonicalizeCharacterDescriptions[cds.rest]; RefTab.Update[x: cdsTab, key: cds, action: UpdateCDS]; RETURN [cds]; }; ftSeq: FactoredSeq ¬ NIL; TransformationIndex: PROC [m: Transformation] RETURNS [NAT] = { FOR i: NAT IN [0..ftSeq.size) DO IF ftSeq[i].m = m THEN RETURN [i]; ENDLOOP; ERROR; }; CDCellSeqRep: TYPE = RECORD[SEQUENCE size: NAT OF CharacterDescriptions]; cdCells: REF CDCellSeqRep ¬ NIL; CDsInOrder: PROC [cds: CharacterDescriptions] RETURNS [BOOL] = { i: NAT ¬ 0; FOR tail: CharacterDescriptions ¬ cds, tail.rest UNTIL tail = NIL DO DO IF i >= ftSeq.size THEN RETURN [FALSE]; IF tail.first.charToDevice = ftSeq[i].m THEN {i ¬ i + 1; EXIT}; i ¬ i + 1; ENDLOOP; ENDLOOP; RETURN [TRUE] }; SortCharacterDescriptions: CardTab.EachPairAction = { cds: CharacterDescriptions ¬ NARROW[val]; result: CharacterDescriptions ¬ NIL; IF CDsInOrder[cds] THEN RETURN; FOR i: NAT IN [0..cdCells.size) DO cdCells[i] ¬ NIL; ENDLOOP; FOR tail: CharacterDescriptions ¬ cds, tail.rest UNTIL tail = NIL DO j: NAT = TransformationIndex[tail.first.charToDevice]; cdCells[j] ¬ NEW[CharacterDescriptionCell ¬ [first: tail.first, rest: cdCells[j]]]; -- don't reuse list cell; it may be shared! ENDLOOP; FOR i: NAT DECREASING IN [0..cdCells.size) DO cell: CharacterDescriptions ¬ cdCells[i]; IF cell # NIL THEN { WHILE cell.rest # NIL AND cell.first = cell.rest.first DO cell ¬ cell.rest; ENDLOOP; IF cell.rest # NIL THEN { tieBreaker: CharacterDescription ¬ SIGNAL Conflict[key, cell]; cell ¬ NEW[CharacterDescriptionCell ¬ [first: tieBreaker, rest: NIL]]; IF cell.first.charToDevice # ftSeq[i].m THEN ERROR; }; cell.rest ¬ result; result ¬ cell; }; ENDLOOP; [] ¬ CardTab.Store[x: fdTable.cardTab, key: key, val: result]; }; CanonicalizeEachCharacterDescriptionList: CardTab.EachPairAction = { cds: CharacterDescriptions = NARROW[val]; IF NOT RefTab.Fetch[x: cdsTab, key: cds].found THEN { canon: CharacterDescriptions = CanonicalizeCharacterDescriptions[cds]; IF canon # cds THEN [] ¬ CardTab.Store[x: fdTable.cardTab, key: key, val: canon]; }; }; MapCharacterDescriptions[fdTable, CanonicalizeCharacterDescription]; ftSeq ¬ SortTransformations[charToDeviceTab]; cdCells ¬ NEW[CDCellSeqRep[ftSeq.size]]; [] ¬ CardTab.Pairs[x: fdTable.cardTab, action: SortCharacterDescriptions]; [] ¬ CardTab.Pairs[x: fdTable.cardTab, action: CanonicalizeEachCharacterDescriptionList]; fdTable.charToDeviceTransformations ¬ NEW[TransformationSeqRep[ftSeq.size]]; FOR i: NAT IN [0..ftSeq.size) DO fdTable.charToDeviceTransformations[i] ¬ ftSeq[i].m; ENDLOOP; }; Factored: TYPE ~ REF FactoredRep; FactoredRep: TYPE ~ RECORD [ f: ImagerTransformation.FactoredTransformation, m: ImagerTransformation.Transformation ]; FactoredSeq: TYPE ~ REF FactoredSeqRep; FactoredSeqRep: TYPE ~ RECORD [SEQUENCE size: NAT OF Factored]; nullFactored: Factored = NEW [FactoredRep ¬ [ImagerTransformation.Factor[identity], NIL]]; SortTransformations: PROC [mTab: RefTab.Ref] RETURNS [FactoredSeq] = { n: NAT = RefTab.GetSize[mTab]; ftSeq: FactoredSeq ¬ NEW[FactoredSeqRep[n]]; len: NAT ¬ 0; SortedInsert: RefTab.EachPairAction = { m: Transformation ¬ NARROW[val]; f: Factored ¬ IF m = NIL THEN nullFactored ELSE NEW [FactoredRep ¬ [ImagerTransformation.Factor[m], m]]; FOR i: NAT IN [0..len) DO t: Factored ¬ ftSeq[i]; SELECT FactoredCompare[f, t] FROM less => {ftSeq[i] ¬ f; f ¬ t}; equal => Debug[f, t]; -- should be unique; else CanonicalizeTransformation is broken, or perhaps floating-point funnies got us. Treat like greater for production, though, since we don't want to die here. greater => NULL; ENDCASE => ERROR; ENDLOOP; ftSeq[len] ¬ f; len ¬ len + 1; }; [] ¬ RefTab.Pairs[x: mTab, action: SortedInsert]; RETURN [ftSeq] }; Debug: PROC [a, b: REF] ~ { ENABLE ABORTED => CONTINUE; ERROR }; FactoredCompare: PROC [b, a: Factored] RETURNS [c: Basics.Comparison ¬ less] = { Differ: PROC [s, t: REAL] RETURNS [BOOL] = INLINE { c ¬ CompareReal[s, t]; RETURN [c # equal] }; IF a = NIL THEN RETURN [IF b = NIL THEN equal ELSE greater]; IF b = NIL THEN RETURN [less]; IF Differ[MAX[ABS[a.f.s.x], ABS[a.f.s.y]], MAX[ABS[b.f.s.x], ABS[b.f.s.y]]] OR Differ[a.f.r1+a.f.r2, b.f.r1+b.f.r2] OR Differ[a.f.s.x, b.f.s.x] OR Differ[a.f.s.y, b.f.s.y] OR Differ[a.f.r1, b.f.r1] OR Differ[a.f.r2, b.f.r2] OR Differ[a.f.t.x, b.f.t.x] OR Differ[a.f.t.y, b.f.t.y] THEN RETURN; IF ImagerTransformation.Equal[a.m, b.m] THEN RETURN[equal]; RETURN [greater] -- Note: in this case, the transformations are so close to equal that the factored representations are the same, due to floating-point fuzz. We return "greater" to disambiguate the sort. }; Read: PUBLIC PROC [filename: Rope.ROPE] RETURNS [fdTable: FDTable] = { stream: IO.STREAM = ImagerSys.OpenInputFile[fileName: filename]; data: Data = FromStream[stream]; fdTable ¬ FDTableFromFDTypefaceData[data]; IO.Close[stream]; }; FDTableFromFDTypefaceData: PUBLIC PROC [data: Data] RETURNS [FDTable] = { fdTable: FDTable = Create[]; FOR set: CharSet IN CharSet DO FOR charRuns: CharRuns ¬ data.setTable[set], charRuns.link UNTIL charRuns = NIL DO offset: CARD = set * 256; FOR tc: NAT IN [0..charRuns.nt) DO charRunEntry: CharRunEntry = charRuns[tc]; charInfoRun: CharInfoRun = data.charInfoRunSeq[charRunEntry.charInfoRunIndex]; cdr: CharacterDescriptionRep ¬ [ typefaceCell: data.typefaceCellSeq[charRunEntry.typefaceIndex], charToDevice: data.charToDeviceTransformations[tc], xcCodeDelta: charInfoRun.xcCodeDelta, bits: charInfoRun.bits, escapement: [0, 0], extents: [0, 0, 0, 0], placement: data.placementTransformations[charInfoRun.placementCode] ]; delta: NAT = IF charInfoRun.size = 1 THEN 0 ELSE 1; i: NAT ¬ 0; FOR code: BYTE IN [charRuns.bc..charRuns.ec] DO WITH charInfoRun[i] SELECT FROM info: CharInfo.short => { cdr.escapement.x ¬ data.commonEscapements[info.escapementX]; cdr.escapement.y ¬ data.commonEscapements[info.escapementY]; cdr.extents.leftExtent ¬ data.commonLeftExtents[info.leftExtent]; cdr.extents.rightExtent ¬ data.commonRightExtents[info.rightExtent]; cdr.extents.descent ¬ data.commonDescents[info.descent]; cdr.extents.ascent ¬ data.commonAscents[info.ascent]; cdr.propList ¬ NIL; }; info: CharInfo.verbose => { v: VerboseCharInfo = data.overflowCharInfoSeq[info.overflowIndex]; cdr.escapement ¬ v.escapement; cdr.extents ¬ v.extents; cdr.propList ¬ v.propList; }; ENDCASE => ERROR; Insert[fdTable, offset+code, cdr]; i ¬ i + delta; ENDLOOP; ENDLOOP; ENDLOOP; ENDLOOP; RETURN [fdTable] }; Write: PUBLIC PROC [fdTable: FDTable, filename: Rope.ROPE] = { Canonicalize[fdTable]; WriteFDTypefaceData[FDTypefaceDataFromCanonicalizedFDTable[fdTable], filename]; }; WriteFDTypefaceData: PUBLIC PROC [data: ImagerFDTypeface.Data, filename: Rope.ROPE] = { stream: IO.STREAM = ImagerSys.OpenOutputFile[fileName: filename]; WriteFDTypefaceDataToStream[data, stream]; IO.Close[stream]; }; WriteFDTypefaceDataToStream: PROC [data: Data, stream: IO.STREAM] = { nSets: NAT ¬ 0; PutCARD32[stream, password]; Begin[stream]; PutExtents[stream, data.fontExtents]; End[stream]; PutRealSeq[stream, data.commonLeftExtents]; PutRealSeq[stream, data.commonRightExtents]; PutRealSeq[stream, data.commonDescents]; PutRealSeq[stream, data.commonAscents]; PutRealSeq[stream, data.commonEscapements]; PutTransformationSeq[stream, data.charToDeviceTransformations]; PutTypefaceCellSeq[stream, data.typefaceCellSeq]; PutTransformationSeq[stream, data.placementTransformations]; PutCharInfoRunSeq[stream, data.charInfoRunSeq]; PutOverflowCharInfoSeq[stream, data.overflowCharInfoSeq]; PutPropList[stream, data.propList]; FOR c: CharSet IN CharSet DO charRuns: CharRuns = data.setTable[c]; IF charRuns # NIL THEN { nSets ¬ nSets+1; }; ENDLOOP; PutCount[stream, nSets]; FOR c: CharSet IN CharSet DO charRuns: CharRuns = data.setTable[c]; IF charRuns # NIL THEN { PutCount[stream, c]; Begin[stream]; PutCharRuns[stream, charRuns]; End[stream]; }; ENDLOOP; }; PutExtents: PROC [stream: IO.STREAM, extents: ImagerBox.Extents] = { PutREAL[stream, extents.leftExtent]; PutREAL[stream, extents.rightExtent]; PutREAL[stream, extents.descent]; PutREAL[stream, extents.ascent]; }; PutRealSeq: PROC [stream: IO.STREAM, s: RealSeq] = { PutCount[stream, s.size]; FOR i: NAT IN [0..s.size) DO Begin[stream]; PutREAL[stream, s[i]]; End[stream]; ENDLOOP; }; PutTransformationSeq: PROC [stream: IO.STREAM, s: TransformationSeq] = { PutCount[stream, s.size]; FOR i: NAT IN [0..s.size) DO Begin[stream]; PutTransformation[stream, s[i]]; End[stream]; ENDLOOP; }; PutTransformation: PROC [stream: IO.STREAM, m: Transformation] = { IF m = NIL THEN PutCount[stream, 0] ELSE { PutCount[stream, 6]; PutREAL[stream, m.a]; PutREAL[stream, m.b]; PutREAL[stream, m.c]; PutREAL[stream, m.d]; PutREAL[stream, m.e]; PutREAL[stream, m.f]; }; }; PutTypefaceCellSeq: PROC [stream: IO.STREAM, s: TypefaceCellSeq] = { PutCount[stream, s.size]; FOR i: NAT IN [0..s.size) DO Begin[stream]; PutTypefaceCell[stream, s[i]]; End[stream]; ENDLOOP; }; PutCharInfoRunSeq: PROC [stream: IO.STREAM, s: CharInfoRunSeq] = { PutCount[stream, s.size]; FOR i: NAT IN [0..s.size) DO Begin[stream]; PutCharInfoRun[stream, s[i]]; End[stream]; ENDLOOP; }; PutTypefaceCell: PROC [stream: IO.STREAM, s: TypefaceCell] = { PutCARD32[stream, LOOPHOLE[s.createDate]]; PutIdentifier[stream, s.fontName]; PutIdentifier[stream, Atom.GetPName[s.type]]; }; PutOverflowCharInfoSeq: PROC [stream: IO.STREAM, s: OverflowCharInfoSeq] = { PutCount[stream, s.size]; FOR i: NAT IN [0..s.size) DO Begin[stream]; PutVerboseCharInfo[stream, s[i]]; End[stream]; ENDLOOP; }; PutVerboseCharInfo: PROC [stream: IO.STREAM, s: VerboseCharInfo] = { PutExtents[stream, s.extents]; PutVEC[stream, s.escapement]; PutPropList[stream, s.propList]; }; PutVEC: PROC [stream: IO.STREAM, v: Vector2.VEC] = { PutREAL[stream, v.x]; PutREAL[stream, v.y]; }; PutPropList: PROC [stream: IO.STREAM, props: Prop.PropList] = { IF props = NIL THEN PutCount[stream, 0] ELSE ERROR; -- unimplemented }; PutCharInfoRun: PROC [stream: IO.STREAM, charInfoRun: CharInfoRun] = { PutInteger[stream, charInfoRun.xcCodeDelta]; PutBYTE[stream, ORD[charInfoRun.bits.correction]*2+ORD[charInfoRun.bits.amplified]]; PutCount[stream, charInfoRun.placementCode]; PutCount[stream, charInfoRun.size]; FOR i: NAT IN [0..charInfoRun.size) DO Begin[stream]; WITH charInfoRun[i] SELECT FROM info: CharInfo.short => { PutBYTE[stream, info.leftExtent]; -- cannot be 255, to discriminate from verbose PutBYTE[stream, info.rightExtent]; PutBYTE[stream, info.descent]; PutBYTE[stream, info.ascent]; PutBYTE[stream, info.escapementX]; PutBYTE[stream, info.escapementY]; }; info: CharInfo.verbose => { PutBYTE[stream, 255]; -- CharInfo.short.leftExtent is never 255 PutCount[stream, info.overflowIndex]; }; ENDCASE => ERROR; End[stream]; ENDLOOP; }; PutCharRuns: PROC [stream: IO.STREAM, charRuns: CharRuns] = { count: CARD ¬ 0; FOR tail: CharRuns ¬ charRuns, tail.link UNTIL tail = NIL DO count ¬ count + 1; ENDLOOP; PutCount[stream, count]; FOR this: CharRuns ¬ charRuns, this.link UNTIL this = NIL DO Begin[stream]; PutBYTE[stream, this.bc]; PutBYTE[stream, this.ec]; FOR i: NAT IN [0..this.nt) DO charRunEntry: CharRunEntry = this[i]; Begin[stream]; PutCount[stream, charRunEntry.typefaceIndex]; PutCount[stream, charRunEntry.charInfoRunIndex]; End[stream]; ENDLOOP; End[stream]; ENDLOOP; }; Begin: PROC [stream: IO.STREAM] = INLINE {}; End: PROC [stream: IO.STREAM] = INLINE {}; PutCARD32: PROC [stream: IO.STREAM, card: CARD32] = { ln: Basics.LongNumber = [lc[card]]; IO.PutByte[stream, ln.hh]; IO.PutByte[stream, ln.hl]; IO.PutByte[stream, ln.lh]; IO.PutByte[stream, ln.ll]; }; PutBYTE: PROC [stream: IO.STREAM, byte: BYTE] = { IO.PutByte[stream, byte]; }; PutCount: PROC [stream: IO.STREAM, count: CARD] = { c: ARRAY [0..5) OF BYTE ¬ ALL[0]; k: NAT ¬ 0; i: CARD ¬ count; DO c[k] ¬ (i MOD 128) + 128; k ¬ k + 1; i ¬ i / 128; IF i = 0 THEN EXIT; ENDLOOP; c[0] ¬ c[0]-128; UNTIL k = 0 DO k ¬ k - 1; IO.PutByte[stream, c[k]]; ENDLOOP; }; PutIdentifier: PROC [stream: IO.STREAM, s: ROPE] = { PutCount[stream, Rope.Size[s]]; IO.PutRope[stream, s]; }; PutInteger: PROC [stream: IO.STREAM, int: INT] = { PutCount[stream, IF int < 0 THEN CARD[CARD[-int]*2-1] ELSE CARD[int]*2]; }; largestFlonumFraction: REAL ¬ LargestFlonumFraction[]; LargestFlonumFraction: PROC RETURNS [REAL] ~ INLINE { a: REAL ¬ 0.5; FOR i: NAT IN [0..1000) DO b: REAL ~ a+a+0.5; IF b = REAL[Real.Fix[b]] THEN RETURN [a]; a ¬ b; ENDLOOP; ERROR; -- this doesn't act like floating point! }; PutREAL: PROC [stream: IO.STREAM, real: REAL] = { neg: BOOL = real < 0; exponent: INT ¬ 0; significand: INT; real ¬ ABS[real]; WHILE real > largestFlonumFraction DO real ¬ Real.FScale[real, -1]; exponent ¬ exponent + 1; ENDLOOP; UNTIL real = (significand ¬ Real.Round[real]) DO real ¬ Real.FScale[real, 1]; exponent ¬ exponent - 1; ENDLOOP; IF neg THEN significand ¬ -significand; PutInteger[stream, significand]; PutInteger[stream, exponent]; }; END. ( ImagerFDBuildImpl.mesa Copyright Σ 1989, 1990, 1991 by Xerox Corporation. All rights reserved. Created by Michael Plass, May 19, 1989 Michael Plass, June 23, 1992 4:42 pm PDT Doug Wyatt, June 5, 1990 3:49 pm PDT CompareReal Construction [found: BOOL, val: CardTab.Val] RETURNS [op: CardTab.UpdateOperation _ none, new: CardTab.Val _ NIL] Canonicalization [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] [found: BOOL, val: CardTab.Val] RETURNS [op: CardTab.UpdateOperation _ none, new: CardTab.Val _ NIL] [key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOL _ FALSE] A very simple cluster analysis. [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] [key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOL _ FALSE] To qualify as a run, the character descriptions must: have contiguous, defined character codes have the same character set have the same set of charToDeviceTransformations have the same typefaceCell, xcCodeDelta, amplified, correction, placement [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL] [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE] [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE] [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL] [key: RefTab.Key] RETURNS [CARDINAL] -- [key1, key2: Key] RETURNS [BOOL] [key: RefTab.Key] RETURNS [CARDINAL] -- [key1, key2: Key] RETURNS [BOOL] [key: RefTab.Key] RETURNS [CARDINAL] -- [key1, key2: Key] RETURNS [BOOL] [key: RefTab.Key] RETURNS [CARDINAL] -- [key1, key2: Key] RETURNS [BOOL] Golly, these variant records are ugly! [key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOL _ FALSE] [found: BOOL, val: SymTab.Val] RETURNS [op: SymTab.UpdateOperation _ none, new: SymTab.Val _ NIL] [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL] [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL] [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL] [key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOL _ FALSE] -- Exact duplicates are OK [key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOL _ FALSE] [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE] Defines the sort-order for charToDeviceTransformations. Note the parameters are [b, a] to end up sorting by decreasing size. Input Output Output Basics Big-endian cardinal ::= lowCardinal | ( hiByte cardinal ) lowCardinal ::= bit0 bit bit bit bit bit bit bit hiByte ::= bit1 bit bit bit bit bit bit bit Non-negative integer, encoded in a variable number of bytes; the high-order bit of each byte is a continuation marker, and the low 7 bits are bits of the encoded number. The first byte contains the most significant bits of the number. Signed integer, encoded in a variable number of bytes; encoded as the cardinal IF int < 0 THEN (-int)*2-1 ELSE int*2 (i.e., uses the low-order bit to indicate the sign) Encode as two signed integers: the significand and the base-two exponent. Κ>X•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Οeœ=™HK™&K™(K™$K™—šΟk ˜ Kšœžœ ˜Kšœžœ'˜3KšœžœR˜_Kšœ žœ ˜Kšœžœw˜ŠKšœžœŠ˜ Kšœ žœ!˜0Kšœžœ@˜ZKšžœžœžœ˜+Kšœžœ˜'Kšœžœ ˜Kšœžœ˜ Kšœžœi˜uKšœžœžœ˜Kšœžœ%˜1Kšœžœžœ˜—K˜KšΟnœžœž˜ KšžœJžœ"˜vKšžœ˜šœžœžœ!˜-K˜Kšžœžœžœ˜šœžœ'˜;K˜—Kš Ÿœžœžœ žœžœžœ˜k—head™ šŸ œžœžœžœ˜>šžœžœžœž˜Kšœ˜Kšœ˜Kšžœ ˜—Kšœ˜——™ Kšœžœ Οc-˜Kšœžœ 1˜ZK˜—šŸœžœžœžœ˜)Kšžœžœ+˜6Kšœ˜K˜—šŸœžœžœžœ"˜S•StartOfExpansionh -- [found: BOOL, val: CardTab.Val] RETURNS [op: CardTab.UpdateOperation _ none, new: CardTab.Val _ NIL]šŸœ˜Kšœžœžœ9žœ™dKšœ žœ$žœ&žœ˜~Kšžœ˜K˜—K–F[x: CardTab.Ref, key: CardTab.Key, action: CardTab.UpdateAction]šœ=˜=Kšœ&žœ˜*Kšœ˜K˜—š Ÿ œžœžœžœžœ˜YKšžœžœ+˜9Kšœ˜K˜—šŸ œžœžœžœ!˜UK–:[x: CardTab.Ref, key: CardTab.Key, val: CardTab.Val]šœ<˜ -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]šŸœ˜&Kš œžœžœžœžœžœ™:Kšœžœ žœ˜ Kšœžœ žœ˜ Kšžœ& (˜TK˜—šŸ œžœžœ˜2–h -- [found: BOOL, val: CardTab.Val] RETURNS [op: CardTab.UpdateOperation _ none, new: CardTab.Val _ NIL]šŸœ˜Kšœžœžœ9žœ™dšžœ˜Kšžœžœ žœ˜ -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]šŸœ˜+Kš œžœžœžœžœžœ™:Kšœžœ žœ˜ Kšœžœ žœ˜ Kšžœ ˜@K˜—š Ÿ œžœžœžœžœ˜1Kšžœžœžœžœ˜%Kšžœžœ˜Kšœ˜—KšœA˜Ašžœžœžœžœžœžœžœž˜?Kšœžœ žœ ˜)šžœž˜Kšžœ˜šžœ˜šžœ*˜,šžœ˜Kšœ$˜$Kšœ˜Kšœ2˜2Kšœ˜—šžœ˜Kšœžœ˜Kšœ˜Kšœ˜——Kšœ˜——Kšžœ˜—Kš žœžœžœžœ$žœ˜PK˜ Kšœ˜—Kšœ<˜žœ˜NKšžœžœžœžœžœžœžœ˜CKšœ˜K˜—Kšœžœžœ˜+Kšœžœžœ žœžœžœžœžœžœžœ˜]šŸœžœžœ˜GKšœžœ˜5šŸœ˜$Kšœ%žœžœžœ™AKšœ˜Kšœžœžœ˜Kšžœžœžœžœžœžœžœžœžœ˜jKšžœžœžœžœ˜-Kšœžœ˜'K˜—Kšœ9˜9Kšžœ˜Kšœ˜K˜—šŸ œžœžœžœ˜Aš žœžœžœžœžœž˜*Kšœ#˜#Kšœ#˜#Kšžœžœžœžœ˜šžœžœ˜Kšœ ž˜#Kšœ ž˜#Kšœž˜!Kšœž˜Kšœ˜Kšœžœžœžœ˜—K˜ K˜ Kšžœ˜—Kšžœ˜Kšœ˜K˜—š œ žœžœžœ žœ˜8K˜—šŸ œžœ5˜E™5K™(K™K™0KšœI™I—Kšœ!žœ˜%Kšœ žœžœžœ˜Kšœ žœ˜Kšœ žœžœ˜šžœžœ ž˜šžœžœžœ˜"Kš œžœžœžœžœ,˜@š žœžœžœžœž˜Kšœ%˜%šžœžœžœ˜&šžœžœ˜ Kšœžœ ˜-Kšœ žœžœ˜Kšœ˜Kšœ žœ˜Kšœ žœ˜Kšœ˜—Kšœ˜—šžœžœžœ˜Kšœžœ˜Kš žœ žœžœžœ žœ˜9Kšœ˜Kšœ žœ˜!Kšœ žœ˜!Kšœ˜—Kšžœ˜—šžœžœ˜ Kšœžœ ˜-Kšœ žœžœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšžœ˜—Kšœ˜K˜—Kšœ žœžœ˜'šœžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ˜K˜Kšœ˜—šŸœžœ žœ˜[Kšœ,˜,Kšœ-˜-Kšœ)˜)Kšœ(˜(Kšœ,˜,K˜*K˜_Kšœžœ˜*K–e -- [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL]šœžœ˜šŸœ˜*Kšœžœžœ7žœ™ašžœžœžœ˜Kšœžœžœ˜ K˜ Kšœ$˜$Kšœ˜—K˜—šŸœžœžœ žœ˜4Kšœ˜Kš œžœžœžœžœ,˜@Kšœžœ ˜Kšžœ žœ˜šžœžœžœž˜šžœ6žœžœž˜LKšœ&˜&K–C[x: RefTab.Ref, key: RefTab.Key, action: RefTab.UpdateAction]˜2K˜OK–C[x: RefTab.Ref, key: RefTab.Key, action: RefTab.UpdateAction]˜DKšœ2˜2Kšœ4˜4Kšœ,˜,Kšœ*˜*Kšœ,˜,Kšœ,˜,Kšžœ˜—Kšžœ˜—Kšœ˜—–C -- [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE]šŸœ˜-Kšœ#žœžœžœ™?Kšœžœžœžœ˜Kšœžœ˜'K˜—Kšœ#˜#KšœL˜LKšœI˜IKšœA˜AKšœ?˜?KšœK˜KKšœžœ%˜;K–2[x: RefTab.Ref, action: RefTab.EachPairAction]šœ>˜>Kšœ!˜!Kšœ6˜6K˜Zšžœžœžœž˜,K–7[x: RefTab.Ref, key: RefTab.Key, val: RefTab.Val]šœMžœžœ˜[Kšžœ˜—Kšžœ ˜Kšœ˜K˜—šŸœžœ3žœ˜dKšœžœžœžœ5˜HKšžœ˜ Kšœ˜K˜—šŸœžœ*žœžœ˜VKšœžœžœžœ4˜GKšžœ˜ Kšœ˜K˜—šŸ&œžœžœžœ˜_Kšœžœ,˜3Kšœ;˜;KšœB˜BKšœžœ˜K–E[mod: NAT _ 17, equal: RefTab.EqualProc, hash: RefTab.HashProc]šœ[˜[K–e -- [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL]šœžœ˜–C -- [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE]šŸœ˜.Kšœ#žœžœžœ™?Kšœžœ˜'Kšœžœžœžœ˜Kšœ+˜+K˜—šŸœžœžœ žœ˜4Kšœ˜Kš œžœžœžœžœ,˜@Kšœžœ ˜Kšœžœ˜-Kšœ!žœ ˜OKšœ˜Kšœ˜šžœž˜Kšžœ,˜0Kšžœ) ˜I—šžœ ˜ šžœ˜Kšœžœ˜ šžœ:žœžœž˜PKšΟtœ‘˜&Kšœ žœžœ˜9Kšžœ‘œ7žœ žœ˜TKšœ‘œ˜IKšœ ˜ Kšœ‘œ ˜)Kšœ‘œ˜Kšœ‘œ ˜CKšžœ"‘œ˜.Kšžœ˜—Kšœ˜—šžœ˜šžœžœžœž˜Kšœžœ˜ šžœ6žœžœž˜LKš‘œ‘˜&Kšœ žœžœ˜&Kšžœ‘œ7žœ žœ˜Tšžœž˜šžœ˜Kšœ‘œ˜IKšœ"žœ˜AKšœ‘œ ˜)Kšœ‘œ˜Kšœ‘œ ˜CKšœ˜—Kšžœ!˜%—Kšžœ+‘œ˜8Kšžœ˜—Kšžœ˜—Kšœ˜——šžœžœžœ ž˜šžœžœžœ˜!–e -- [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL]šŸœ˜(KšΠcka™ašžœžœžœ˜Kšœ ˜ Kšœžœžœ˜Kšœ"˜"Kšœ˜—K˜—K–C[x: RefTab.Ref, key: RefTab.Key, action: RefTab.UpdateAction]šœR˜RKšœ˜—Kšžœ˜—šžœžœžœ ž˜šžœžœžœ˜!Kšœ<˜šžœž œžœ ž˜0Kšœ1˜1Kšœ!˜!Kšžœ˜—Kšžœ5žœ)˜dKšœ˜K˜—šŸœžœžœžœžœžœžœ žœžœžœ ž œ˜mš žœžœžœžœžœžœž˜;Kšœ˜Kšœžœžœ˜Kšžœ˜—Kšœ˜K˜—šŸœžœ˜5Kšœžœžœ™$Kšœžœ˜&Kšœžœ žœžœ˜FKšžœ˜ K˜K˜—–( -- [key: RefTab.Key] RETURNS [CARDINAL]šŸœ˜/Kšžœžœžœ™#Kšœžœ˜(Kšœžœ˜(Kšžœ ˜K˜K˜—šŸœžœ˜6Kšœžœžœ™$Kšœžœ˜'Kšœžœžœžœ˜+Kš œžœžœžœžœžœžœ˜,Kš œžœ žœžœžœžœžœ˜BKšžœ˜ K˜K˜—–( -- [key: RefTab.Key] RETURNS [CARDINAL]šŸœ˜0Kšžœžœžœ™#Kšœžœ˜)Kšœžœ˜)Kšžœ ˜K˜K˜—–( -- [key: RefTab.Key] RETURNS [CARDINAL]šŸœžœ˜/Kšœžœžœ™$Kšœžœ˜ šžœž˜ Kšžœžœ˜šžœ˜Kšœžœžœžœžœžœžœžœžœžœ˜WKš œžœžœžœžœžœžœ˜-Kš œžœ žœžœžœžœžœ˜CKšžœ˜ Kšœ˜——K˜K˜—–( -- [key: RefTab.Key] RETURNS [CARDINAL]šŸœ˜)Kšžœžœžœ™#Kšœžœ˜"Kšœžœ˜"Kš žœžœžœžœžœ$˜BK˜K˜—–( -- [key: RefTab.Key] RETURNS [CARDINAL]šŸœžœ˜,Kšœžœžœ™$Kšœžœ˜'Kšœžœ žœžœ$˜YKšžœ˜ K˜K˜—–( -- [key: RefTab.Key] RETURNS [CARDINAL]šŸœžœ˜.Kš #™#Kšœžœ˜Kšœžœ˜šžœžœ˜Kšœž˜Kšœ ž˜#Kšœž˜Kšœ#˜#Kšœžœžœžœ˜—šžœžœžœž˜Kšœ˜Kšœ˜K™&šžœžœž˜šœ ˜ šžœžœž˜šœ ˜ Kšžœ žœžœžœ˜Kšœ˜—Kšžœžœžœ˜—Kšœ˜—šœ ˜ šžœžœž˜šœ ˜ Kšžœ žœžœžœ˜Kšœ˜—Kšžœžœžœ˜—Kšœ˜—Kšžœžœ˜—Kšžœ˜—Kšžœžœ˜K˜K˜—šŸœžœžœžœ˜{–E -- [key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOL _ FALSE]šŸœ˜$Kšœ%žœžœžœ™Aš žœžœžœžœž˜LKšœ˜Kšžœ˜—K˜—Kšœ9˜9Kšœ˜K˜—K˜9šŸœžœžœ˜>Kš žœžœžœžœ žœ˜(Kšœ˜K˜—šŸ œžœžœ˜0K–E[mod: NAT _ 17, equal: RefTab.EqualProc, hash: RefTab.HashProc]šœW˜WK–E[mod: NAT _ 17, equal: RefTab.EqualProc, hash: RefTab.HashProc]šœ.˜.Kšœd˜dK–&[mod: NAT _ 17, case: BOOL _ TRUE]šœ(žœ˜/šŸœžœžœ˜V–e -- [found: BOOL, val: SymTab.Val] RETURNS [op: SymTab.UpdateOperation _ none, new: SymTab.Val _ NIL]šŸœ˜!Kšœžœžœ7žœ™ašžœžœ˜ Kšžœžœ˜!šžœ˜Kšœžœ˜"Kšžœžœžœ˜$š žœžœžœžœžœ˜>Kšœ˜Kšœ˜—Kšœ˜Kšœ˜——K˜—šžœžœžœ˜%Kšœ3˜3Kšœ8˜8Kšœ5˜5Kšœ˜—Kš žœžœžœžœ ˜FKšœF˜FKšžœ˜Kšœ˜—šŸœžœžœ˜Q–e -- [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL]šŸœ˜-Kšœžœžœ7žœ™aKš žœžœžœžœžœ ˜4K˜—K˜K˜=Kšžœ˜ Kšœ˜—šŸ œžœžœ˜d–e -- [found: BOOL, val: RefTab.Val] RETURNS [op: RefTab.UpdateOperation _ none, new: RefTab.Val _ NIL]šŸœ˜!Kšœžœžœ7žœ™ašžœ˜Kšžœžœ˜Kšžœžœ ˜—K˜—Kšœ>˜>KšœF˜FKšœ<˜Kšœžœ6žœ˜FKšžœ&žœžœ˜3Kšœ˜—Kšœ˜Kšœ˜Kšœ˜—Kšžœ˜—K–:[x: CardTab.Ref, key: CardTab.Key, val: CardTab.Val]šœ>˜>Kšœ˜—šŸ(œ˜DKšœ%žœžœžœ™AKšœžœ˜)šžœžœ)žœ˜5KšœF˜FK–:[x: CardTab.Ref, key: CardTab.Key, val: CardTab.Val]šžœ žœ>˜QKšœ˜—Kšœ˜—KšœD˜DK–4[x: CardTab.Ref, action: CardTab.EachPairAction]šœ-˜-Kšœ žœ˜(KšœJ˜JKšœY˜YKšœ&žœ#˜Lšžœžœžœž˜ Kšœ4˜4Kšžœ˜—Kšœ˜K˜—Kšœ žœžœ ˜!šœ žœžœ˜Kšœ/˜/Kšœ&˜&Kšœ˜K˜—Kšœ žœžœ˜'š œžœžœžœžœžœ ˜?K˜—šœžœ8žœ˜ZK˜—šŸœžœžœ˜FKšœžœ˜Kšœžœ˜,Kšœžœ˜ šŸ œ˜'Kš’?™?Kšœžœ˜ Kš œžœžœžœžœžœ5˜hšžœžœžœ ž˜Kšœ˜šžœž˜!Kšœ˜Kšœ Ά˜ΜKšœ žœ˜Kšžœžœ˜—Kšžœ˜—Kšœ˜Kšœ˜K˜—Kšœ1˜1Kšžœ˜Kšœ˜K˜—šŸœžœžœžœžœžœžœ˜@K˜—šŸœžœžœ"˜PK™7K™Dš Ÿœžœžœžœžœžœ˜3Kšœ˜Kšžœ ˜Kšœ˜—Kšžœžœžœžœžœžœžœžœ ˜Kšœ˜K–Κ[fileName: ROPE, accessOptions: FS.AccessOptions _ read, streamOptions: FS.StreamOptions _ (3)[TRUE, TRUE, TRUE], keep: CARDINAL _ 1B (1), createByteCount: FS.ByteCount _ 2560, streamBufferParms: FS.StreamBufferParms _ [vmPagesPerBuffer: 8, nBuffers: 2], extendFileProc: FS.ExtendFileProc, wantedCreatedTime: GMT _ nullGMT, remoteCheck: BOOL _ TRUE, wDir: ROPE _ NIL, checkFileType: BOOL _ FALSE, fileType: FS.FileType _ [0B (0)]]šœO˜OKšœ˜K˜—šŸœžœžœ.žœ˜WK–Κ[fileName: ROPE, accessOptions: FS.AccessOptions _ read, streamOptions: FS.StreamOptions _ (3)[TRUE, TRUE, TRUE], keep: CARDINAL _ 1B (1), createByteCount: FS.ByteCount _ 2560, streamBufferParms: FS.StreamBufferParms _ [vmPagesPerBuffer: 8, nBuffers: 2], extendFileProc: FS.ExtendFileProc, wantedCreatedTime: GMT _ nullGMT, remoteCheck: BOOL _ TRUE, wDir: ROPE _ NIL, checkFileType: BOOL _ FALSE, fileType: FS.FileType _ [0B (0)]]šœžœžœ0˜AKš‘œ‘ œ ‘˜*Kšžœ˜Kšœ˜K˜—š ΠntŸ£œžœžœžœ˜EKšœžœ˜Kšœ˜Kšœ˜Kšœ%˜%Kšœ ˜ Kšœ+˜+Kšœ,˜,Kšœ(˜(Kšœ'˜'Kšœ+˜+Kšœ?˜?Kšœ1˜1Kšœ<˜Kšœžœ˜*Kšœ"˜"Kšœ-˜-Kšœ˜K˜—šŸœžœ žœžœ˜LKšœ˜šžœžœžœ ž˜Kšœ˜Kšœ!˜!Kšœ ˜ Kšžœ˜—Kšœ˜K˜—šŸœžœ žœžœ˜DKšœ˜Kšœ˜Kšœ ˜ Kšœ˜K˜—š Ÿœžœ žœžœ žœ˜4Kšœ˜Kšœ˜Kšœ˜K˜—šŸ œžœ žœžœ˜?šžœ ž˜Kšžœ˜Kšžœžœ ˜—Kšœ˜K˜—šŸœžœ žœžœ˜FKšœ,˜,Kšœžœ žœ˜TKš‘œ$˜,Kš‘ œ‘˜#š žœžœžœ‘œž˜&K˜šžœžœž˜šœ˜Kšœ" .˜PKšœ"˜"Kšœ˜Kšœ˜Kšœ"˜"Kšœ"˜"Kšœ˜—šœ˜Kšœ )˜?Kšœ%˜%Kšœ˜—Kšžœžœ˜—K˜ Kšžœ˜—Kšœ˜—K˜šŸ œžœ žœžœ˜=Kšœžœ˜šžœ&žœžœž˜