<> <> <> <> <<>> DIRECTORY Basics USING [BYTE, bytesPerWord], FS, II, IIBackdoor, IIBox, IIFont, IIPath, IISample, IITransformation, IITypeface, PrePressFontFormat, PrincOpsUtils, Real, RealConvert, RefText, Rope, RopeFile, Vector2; IICDTypefaceImpl: CEDAR PROGRAM IMPORTS FS, II, IIBackdoor, IIBox, IISample, IITransformation, IITypeface, PrePressFontFormat, PrincOpsUtils, Real, RealConvert, RefText, Rope, RopeFile, Vector2 ~ BEGIN BYTE: TYPE ~ [0..377B]; VEC: TYPE ~ Vector2.VEC; ROPE: TYPE ~ Rope.ROPE; Extents: TYPE ~ IIFont.Extents; Transformation: TYPE ~ IITransformation.Transformation; XChar: TYPE ~ IIFont.XChar; nullXChar: XChar ~ IIFont.nullXChar; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ setTable: SetTable _ NIL, amplifySpace: BOOL _ FALSE, groups: LIST OF GroupData _ NIL ]; CharSet: TYPE ~ BYTE; SetTable: TYPE ~ REF SetTableRep; SetTableRep: TYPE ~ ARRAY CharSet OF CharTable _ ALL[NIL]; CharTable: TYPE ~ REF CharTableRep; CharTableRep: TYPE ~ RECORD [bc, ec: BYTE, s: SEQUENCE size: NAT OF CharMetrics]; CharMetrics: TYPE ~ REF CharMetricsRep; CharMetricsRep: TYPE ~ RECORD [escapement: VEC, bb: IIBox.Box]; Representation: TYPE ~ { spline, alignedRaster, packedRaster }; GroupData: TYPE ~ REF GroupDataRep; GroupDataRep: TYPE ~ RECORD [ base: ROPE _ NIL, set: BYTE _ 0, bc, ec: BYTE _ 0, representation: Representation, dataByteOffset: INT _ 0, directoryByteOffset: INT _ 0, dataByteLength: INT _ 0, pixelToChar: Transformation _ NIL ]; MalformedCDFont: ERROR ~ CODE; Assert: PROC [truth: BOOL] ~ --INLINE-- { IF NOT truth THEN ERROR MalformedCDFont }; Bytes: PROC [wordSize: NAT] RETURNS [CARDINAL] ~ --INLINE-- {RETURN [wordSize*2]}; check: [2..2] ~ Basics.bytesPerWord; <> FileBytes: PROC [fileWords: INT] RETURNS [INT] ~ --INLINE-- {RETURN [fileWords*2]}; <> RawFetch: UNSAFE PROC [base: ROPE, byteOffset: INT, destination: LONG POINTER, nBytes: NAT] ~ UNCHECKED { buf: REF TEXT ~ RefText.ObtainScratch[nBytes]; Assert[(byteOffset + nBytes) <= Rope.Size[base]]; [] _ Rope.AppendChars[buffer: buf, rope: base, start: byteOffset, len: nBytes]; PrincOpsUtils.LongCopy[from: LOOPHOLE[buf, LONG POINTER]+SIZE[TEXT[0]], nwords: (nBytes+(Basics.bytesPerWord-1))/Basics.bytesPerWord, to: destination]; RefText.ReleaseScratch[buf]; }; rawOptions: FS.StreamOptions ~ [tiogaRead: FALSE, commitAndReopenTransOnFlush: TRUE, truncatePagesOnClose: TRUE, finishTransOnClose: TRUE, closeFSOpenFileOnClose: TRUE]; CDCreate: PROC [file: FS.OpenFile] RETURNS [IITypeface.Typeface] ~ { base: ROPE ~ RopeFile.FromStream[FS.StreamFromOpenFile[openFile: file, streamOptions: rawOptions]]; RETURN [TypefaceFromRope[base]] }; checkForBogusResolution: BOOL _ TRUE; -- There seem to be print services fonts in circulation that have a smashed resolutionX field; if this bool is true, we do some paranoid checks and smash it to 300bpi if it smells funny. tryForCharSet: BOOL _ TRUE; -- The resolutionY field has been re-used in print services fonts to represent the character set; if this bool is true, we use this interpretation after making some reasonableness checks. As long as the resolution is 256 or greater, we can always disambiguate. TypefaceFromRope: PROC [base: ROPE] RETURNS [IITypeface.Typeface] ~ { byteOffset: INT _ 0; data: Data ~ NEW[DataRep _ []]; ix: PrePressFontFormat.IXHeader; indexFamily: BYTE; indexFace: BYTE; nameFound, indexFound: BOOL _ FALSE; groups: LIST OF GroupData _ NIL; DO -- read the index part headerBytes: NAT ~ Bytes[SIZE[PrePressFontFormat.IXHeader]]; TRUSTED { RawFetch[base, byteOffset, @ix, headerBytes] }; SELECT ix.type FROM end => EXIT; name => { name: PrePressFontFormat.NameIndexEntry; nameBytes: NAT ~ Bytes[SIZE[PrePressFontFormat.NameIndexEntry]]; Assert[NOT nameFound]; -- don't allow more than one name entry Assert[FileBytes[ix.length] = headerBytes + nameBytes]; TRUSTED { RawFetch[base, byteOffset+headerBytes, @name, nameBytes] }; nameFound _ TRUE; IF indexFound THEN Assert[name.code=indexFamily] ELSE indexFamily _ name.code; }; spline => { index: PrePressFontFormat.StdIndexEntry; sdixBytes: NAT ~ Bytes[SIZE[PrePressFontFormat.StdIndexEntry]]; group: GroupData ~ NEW[GroupDataRep]; segmentByteIndex, segmentBytes: INT _ 0; group.representation _ spline; group.set _ 0; Assert[FileBytes[ix.length] = headerBytes + sdixBytes]; TRUSTED { RawFetch[base, byteOffset+headerBytes, @index, sdixBytes] }; Assert[index.bc <= index.ec]; group.base _ base; group.bc _ index.bc; group.ec _ index.ec; group.dataByteOffset _ FileBytes[PrePressFontFormat.CardFromBcpl[index.segmentSA]]; group.dataByteLength _ FileBytes[PrePressFontFormat.CardFromBcpl[index.segmentLength]]; Assert[group.dataByteOffset + group.dataByteLength <= Rope.Size[base]]; group.directoryByteOffset _ group.dataByteOffset + (index.ec-index.bc+1) * Bytes[SIZE[PrePressFontFormat.SplineData]]; group.pixelToChar _ IITransformation.Rotate[-index.rotation/60.0]; IF indexFound THEN Assert[indexFamily=index.family AND indexFace = index.face]; IF nameFound OR indexFound THEN Assert[index.family=indexFamily]; indexFamily _ index.family; indexFace _ index.face; indexFound _ TRUE; groups _ CONS[group, groups]; <> <> <> <<}>> <> <> }; character => { index: PrePressFontFormat.CharacterIndexEntry; cixBytes: NAT ~ Bytes[SIZE[PrePressFontFormat.CharacterIndexEntry]]; group: GroupData ~ NEW[GroupDataRep]; charUnitsPerResolutionUnit: REAL _ 0; group.representation _ alignedRaster; group.set _ 0; Assert[FileBytes[ix.length] = headerBytes + cixBytes]; TRUSTED { RawFetch[base, byteOffset+headerBytes, @index, cixBytes] }; IF checkForBogusResolution THEN { IF index.resolutionX = 3840 OR index.resolutionX MOD 1500 = 0 THEN NULL ELSE index.resolutionX _ 3000 }; IF tryForCharSet THEN { IF index.resolutionY MOD 10 = 0 AND index.resolutionY/10 IN CharSet THEN { group.set _ index.resolutionY/10; index.resolutionY _ index.resolutionX; }; }; Assert[index.bc <= index.ec]; group.base _ base; group.bc _ index.bc; group.ec _ index.ec; group.dataByteOffset _ FileBytes[PrePressFontFormat.CardFromBcpl[index.segmentSA]]; group.dataByteLength _ FileBytes[PrePressFontFormat.CardFromBcpl[index.segmentLength]]; Assert[group.dataByteOffset + group.dataByteLength <= Rope.Size[base]]; group.directoryByteOffset _ group.dataByteOffset + (index.ec-index.bc+1) * Bytes[SIZE[PrePressFontFormat.CharacterData]]; charUnitsPerResolutionUnit _ 25400.0/index.size; -- units of resolution are dots per 10 inches group.pixelToChar _ IITransformation.Scale2[[ charUnitsPerResolutionUnit/index.resolutionX, charUnitsPerResolutionUnit/index.resolutionY ]]; IF index.rotation#0 THEN group.pixelToChar.ApplyPreRotate[-index.rotation/60.0]; IF indexFound THEN Assert[indexFamily=index.family AND indexFace = index.face]; IF nameFound OR indexFound THEN Assert[index.family=indexFamily]; indexFamily _ index.family; indexFace _ index.face; indexFound _ TRUE; groups _ CONS[group, groups]; }; ENDCASE => ERROR MalformedCDFont; -- unexpected ix type byteOffset _ byteOffset + FileBytes[ix.length]; ENDLOOP; Assert[nameFound AND indexFound]; data.groups _ SortGroups[groups]; data.setTable _ BuildSetTable[data.groups]; IF data.setTable[0] # NIL AND data.setTable[0][40B] # NIL AND data.setTable[0][40B].bb = [0.0, 0.0, 0.0, 0.0] THEN data.amplifySpace _ TRUE; RETURN[NEW[IITypeface.TypefaceRep _ [class: acClass, data: data]]]; }; InOrder: PROC [a, b: GroupData] RETURNS [BOOL] ~ { SELECT TRUE FROM a.set # b.set => RETURN [a.set < b.set]; a.representation # b.representation AND (a.representation=spline OR b.representation=spline) => RETURN [a.representation < b.representation]; ENDCASE => { sva: VEC ~ IITransformation.SingularValues[a.pixelToChar]; svb: VEC ~ IITransformation.SingularValues[b.pixelToChar]; IF sva.x < svb.x THEN RETURN [TRUE]; IF sva.x = svb.x AND sva.y < svb.y THEN RETURN [TRUE]; RETURN [FALSE]; }; }; SortGroups: PROC [groups: LIST OF GroupData] RETURNS [LIST OF GroupData] ~ { unconsumed: LIST OF GroupData _ groups; new: LIST OF GroupData _ NIL; WHILE unconsumed # NIL DO rest: LIST OF GroupData ~ unconsumed.rest; current: GroupData ~ unconsumed.first; a: LIST OF GroupData _ new; p: LIST OF GroupData _ NIL; WHILE a#NIL AND NOT InOrder[a.first, current] DO p _ a; a _ a.rest ENDLOOP; IF p = NIL THEN { unconsumed.rest _ new; new _ unconsumed } ELSE { unconsumed.rest _ p.rest; p.rest _ unconsumed }; unconsumed _ rest; ENDLOOP; RETURN [new]; }; BuildSetTable: PROC [groups: LIST OF GroupData] RETURNS [SetTable] ~ { setTable: SetTable ~ NEW[SetTableRep _ ALL[NIL]]; setbc: ARRAY CharSet OF BYTE _ ALL[LAST[BYTE]]; setec: ARRAY CharSet OF BYTE _ ALL[FIRST[BYTE]]; FOR each: LIST OF GroupData _ groups, each.rest UNTIL each = NIL DO group: GroupData ~ each.first; set: BYTE ~ group.set; setbc[set] _ MIN[group.bc, setbc[set]]; setec[set] _ MAX[group.ec, setec[set]]; ENDLOOP; FOR set: CharSet IN CharSet DO IF setbc[set] <= setec[set] THEN { charTable: CharTable ~ NEW[CharTableRep[setec[set]-setbc[set]+1]]; charTable.bc _ setbc[set]; charTable.ec _ setec[set]; FOR i: NAT IN [0..charTable.size) DO charTable[i] _ NIL ENDLOOP; setTable[set] _ charTable; }; ENDLOOP; FOR each: LIST OF GroupData _ groups, each.rest UNTIL each = NIL DO group: GroupData ~ each.first; charTable: CharTable ~ setTable[group.set]; FOR code: BYTE IN [group.bc..group.ec] DO SELECT group.representation FROM spline => { sd: PrePressFontFormat.SplineData ~ GetSplineData[group, code]; IF sd.wx#PrePressFontFormat.missingSpline THEN { wx: REAL ~ RealConvert.BcplToIeee[sd.wx]; wy: REAL ~ RealConvert.BcplToIeee[sd.wy]; e: VEC ~ IITransformation.TransformVec[group.pixelToChar, [wx, wy]]; wbb: IIBox.Box ~ [xmin: RealConvert.BcplToIeee[sd.xmin], ymin: RealConvert.BcplToIeee[sd.ymin], xmax: RealConvert.BcplToIeee[sd.xmax], ymax: RealConvert.BcplToIeee[sd.ymax]]; box: IIBox.Box ~ TransformBox[group.pixelToChar, wbb]; charMetrics: CharMetrics ~ charTable[code-charTable.bc]; IF charMetrics = NIL THEN charTable[code-charTable.bc] _ NEW [CharMetricsRep _ [escapement: e, bb: box]] ELSE charMetrics.bb _ IIBox.BoundingBox[charMetrics.bb, box]; }; }; alignedRaster => { cd: PrePressFontFormat.CharacterData ~ GetCharacterData[group, code]; IF cd.bbdy#PrePressFontFormat.missingCharacter THEN { wx: REAL ~ Real.FScale[PrePressFontFormat.IntFromBcpl[cd.wx], -16]; wy: REAL ~ Real.FScale[PrePressFontFormat.IntFromBcpl[cd.wy], -16]; e: VEC ~ IITransformation.TransformVec[group.pixelToChar, [wx, wy]]; box: IIBox.Box ~ IIBox.BoxFromRectangle[IITransformation.TransformRectangle[group.pixelToChar, [x: cd.bbox, y: cd.bboy, w: cd.bbdx, h: cd.bbdy]]]; charMetrics: CharMetrics ~ charTable[code-charTable.bc]; IF charMetrics = NIL THEN charTable[code-charTable.bc] _ NEW [CharMetricsRep _ [escapement: e, bb: box]] ELSE charMetrics.bb _ IIBox.BoundingBox[charMetrics.bb, box]; }; }; ENDCASE => ERROR; ENDLOOP; ENDLOOP; RETURN [setTable] }; TransformBox: PROC [m: Transformation, box: IIBox.Box] RETURNS [IIBox.Box] ~ { RETURN [IIBox.BoxFromRectangle[IITransformation.TransformRectangle[m, IIBox.RectangleFromBox[box]]]] }; GetCharacterData: PROC [group: GroupData, code: BYTE] RETURNS [PrePressFontFormat.CharacterData] ~ { characterData: PrePressFontFormat.CharacterData; cdBytes: INT ~ Bytes[SIZE[PrePressFontFormat.CharacterData]]; IF group.representation # alignedRaster THEN ERROR; characterData.bbdy _ PrePressFontFormat.missingCharacter; IF code IN [group.bc..group.ec] THEN TRUSTED { RawFetch[group.base, group.dataByteOffset + (code-group.bc) * cdBytes, @characterData, cdBytes] }; RETURN [characterData]; }; GetSplineData: PROC [group: GroupData, code: BYTE] RETURNS [PrePressFontFormat.SplineData] ~ { splineData: PrePressFontFormat.SplineData; splineDataBytes: INT ~ Bytes[SIZE[PrePressFontFormat.SplineData]]; IF group.representation # spline THEN ERROR; splineData.wx _ PrePressFontFormat.missingSpline; IF code IN [group.bc..group.ec] THEN TRUSTED { RawFetch[group.base, group.dataByteOffset + (code-group.bc) * splineDataBytes, @splineData, splineDataBytes] }; RETURN [splineData]; }; CDContains: PROC [self: IITypeface.Typeface, char: IIFont.XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; charTable: CharTable ~ data.setTable[char.set]; IF charTable = NIL OR char.code NOT IN [charTable.bc..charTable.ec] THEN RETURN [FALSE]; RETURN [charTable[char.code-charTable.bc] # NIL]; }; CDNextChar: PROC [self: IITypeface.Typeface, char: IIFont.XChar] RETURNS [IIFont.XChar] ~ { data: Data ~ NARROW[self.data]; next: XChar _ IF char = nullXChar THEN [0, 0] ELSE IF char.code = LAST[BYTE] THEN [set: char.set+1, code: FIRST[BYTE]] ELSE [set: char.set, code: char.code+1]; charTable: CharTable _ data.setTable[next.set]; UNTIL next = nullXChar OR (charTable # NIL AND next.code IN [charTable.bc..charTable.ec] AND charTable[char.code-charTable.bc] # NIL) DO SELECT TRUE FROM charTable = NIL OR next.code > charTable.ec OR next.code = LAST[BYTE] => { next _ IF next.set = nullXChar.set THEN nullXChar ELSE [next.set+1, 0]; charTable _ data.setTable[next.set]; }; next.code < charTable.bc => next.code _ charTable.bc; ENDCASE => next.code _ next.code + 1; ENDLOOP; RETURN [next]; }; CDEscapement: PROC [self: IITypeface.Typeface, char: IIFont.XChar] RETURNS [VEC] ~ { data: Data ~ NARROW[self.data]; charTable: CharTable ~ data.setTable[char.set]; IF charTable # NIL AND char.code IN [charTable.bc..charTable.ec] THEN { charMetrics: CharMetrics ~ charTable[char.code-charTable.bc]; IF charMetrics # NIL THEN RETURN [charMetrics.escapement] }; RETURN[[0.5, 0]]; }; CDAmplified: PROC [self: IITypeface.Typeface, char: IIFont.XChar] RETURNS [BOOL] ~ { data: Data ~ NARROW[self.data]; RETURN [data.amplifySpace AND char = [set: 0, code: 40B]]; }; CDCorrection: PROC [self: IITypeface.Typeface, char: IIFont.XChar] RETURNS [IIFont.CorrectionType] ~ { data: Data ~ NARROW[self.data]; IF CDContains[self, char] THEN { IF data.amplifySpace AND char = [set: 0, code: 40B] THEN RETURN[space]; RETURN[mask]; }; RETURN[none]; }; CDBoundingBox: PROC [self: IITypeface.Typeface, char: IIFont.XChar] RETURNS [IIFont.Extents] ~ { data: Data ~ NARROW[self.data]; charTable: CharTable ~ data.setTable[char.set]; IF charTable # NIL AND char.code IN [charTable.bc..charTable.ec] THEN { charMetrics: CharMetrics ~ charTable[char.code-charTable.bc]; IF charMetrics # NIL THEN RETURN [IIBox.ExtentsFromBox[charMetrics.bb]]; }; RETURN[[leftExtent: -0.05, rightExtent: 0.45, descent: 0, ascent: 0.6]]; }; CDFontBoundingBox: PROC [self: IITypeface.Typeface] RETURNS [IIFont.Extents] ~ { data: Data ~ NARROW[self.data]; setTable: SetTable ~ data.setTable; bb: IIBox.Box _ [xmin: 0.05, ymin: 0.0, xmax: 0.45, ymax: 0.6]; FOR set: CharSet IN CharSet DO charTable: CharTable ~ setTable[set]; IF charTable # NIL THEN { FOR code: BYTE IN [charTable.bc..charTable.ec] DO charMetrics: CharMetrics ~ charTable[code-charTable.bc]; IF charMetrics # NIL THEN bb _ IIBox.BoundingBox[bb, charMetrics.bb]; ENDLOOP; }; ENDLOOP; RETURN [IIBox.ExtentsFromBox[bb]] }; CDKern: PROC [self: IITypeface.Typeface, char, successor: IIFont.XChar] RETURNS [VEC] ~ { RETURN[[0, 0]] }; CDNextKern: PROC [self: IITypeface.Typeface, char, successor: IIFont.XChar] RETURNS [IIFont.XChar] ~ { RETURN[IIFont.nullXChar] }; CDLigature: PROC [self: IITypeface.Typeface, char, successor: IIFont.XChar] RETURNS [IIFont.XChar] ~ { RETURN[IIFont.nullXChar] }; CDNextLigature: PROC [self: IITypeface.Typeface, char, successor: IIFont.XChar] RETURNS [IIFont.XChar] ~ { RETURN[IIFont.nullXChar] }; GetBitmap: PROC [group: GroupData, code: BYTE] RETURNS [IISample.SampleMap] ~ { sampleMap: IISample.SampleMap _ NIL; cd: PrePressFontFormat.CharacterData ~ GetCharacterData[group, code]; IF cd.bbdy#PrePressFontFormat.missingCharacter THEN TRUSTED { rfpBytes: INT ~ Bytes[SIZE[PrePressFontFormat.RelFilePos]]; rfpBytePos: INT ~ group.directoryByteOffset+(code-group.bc)*rfpBytes; rfp: PrePressFontFormat.RelFilePos; TRUSTED {RawFetch[group.base, rfpBytePos, @rfp, rfpBytes] }; IF rfp # PrePressFontFormat.missingFilePos THEN { relativeByte: INT ~ FileBytes[PrePressFontFormat.IntFromBcpl[rfp]]; rdBytes: INT ~ Bytes[SIZE[PrePressFontFormat.RasterDefn]]; rd: PrePressFontFormat.RasterDefn; RawFetch[group.base, group.directoryByteOffset+relativeByte, @rd, rdBytes]; Assert[rd.lines=cd.bbdx AND rd.raster=(cd.bbdy+15)/16]; IF cd.bbdx>0 AND cd.bbdy>0 THEN { rasterBytes: INT ~ FileBytes[LONG[rd.raster]*rd.lines]; sampleMap _ IISample.ObtainScratchMap[ box: [min: [s: cd.bbox, f: cd.bboy], max: [s: cd.bbdx+cd.bbox, f: cd.bbdy+cd.bboy]], <> bitsPerSample: 1 ]; TRUSTED {RawFetch[group.base, group.directoryByteOffset+relativeByte+rdBytes, IISample.GetBase[sampleMap].word, rasterBytes] }; }; }; }; RETURN [sampleMap]; }; Right: PROC [angle: REAL] RETURNS [BOOL] ~ { WHILE angle < 0 DO angle _ angle + 90.0 ENDLOOP; WHILE angle > 45.0 DO angle _ angle - 90.0 ENDLOOP; RETURN [ABS[angle] <= 1.666667E-2] }; minScale: REAL _ 0.9875; maxScale: REAL _ 1.0125; FindBestGroup: PROC [data: Data, char: IIFont.XChar, t: Transformation] RETURNS [GroupData] ~ { best: GroupData _ NIL; FOR each: LIST OF GroupData _ data.groups, each.rest UNTIL each = NIL DO group: GroupData ~ each.first; IF group.set = char.set THEN { SELECT group.representation FROM spline => {best _ group}; alignedRaster => { cd: PrePressFontFormat.CharacterData ~ GetCharacterData[group, char.code]; IF cd.bbdy#PrePressFontFormat.missingCharacter THEN { composite: Transformation ~ IITransformation.Concat[group.pixelToChar, t]; f: IITransformation.FactoredTransformation ~ IITransformation.Factor[composite]; IITransformation.Destroy[composite]; IF Right[f.r1] AND Right[f.r2] AND ABS[f.s.x] IN [minScale..maxScale] AND ABS[f.s.y] IN [minScale..maxScale] THEN {best _ group; EXIT}; IF best = NIL THEN best _ group; }; }; ENDCASE => ERROR; }; ENDLOOP; IF best = NIL THEN ERROR; -- CDContains lied! RETURN [best]; }; CDMask: PROC [self: IITypeface.Typeface, char: IIFont.XChar, context: II.Context] ~ { data: Data ~ NARROW[self.data]; IF CDContains[self, char] THEN { t: Transformation ~ IIBackdoor.GetTransformation[context: context, from: client, to: device]; group: GroupData ~ FindBestGroup[data, char, t]; IITransformation.Destroy[t]; SELECT group.representation FROM spline => MaskSpline[group, char.code, context]; alignedRaster => { bitmap: IISample.SampleMap _ GetBitmap[group, char.code]; IF bitmap # NIL THEN { II.ConcatT[context, group.pixelToChar]; II.MaskBitmap[context: context, bitmap: bitmap, referencePoint: [0, 0], scanMode: [slow: right, fast: up], position: [0, 0]]; IISample.ReleaseScratchMap[bitmap]; }; }; ENDCASE => ERROR; } ELSE II.MaskRectangle[context, [0.05, 0, 0.4, 0.6]]; }; MaskSpline: PROC [group: GroupData, code: BYTE, context: II.Context] ~ { path: IIPath.PathProc ~ { MapSplineOutline[group: group, code: code, moveTo: moveTo, lineTo: lineTo, curveTo: curveTo] }; II.MaskFill[context: context, path: path, oddWrap: TRUE]; }; MapSplineOutline: PROC [group: GroupData, code: BYTE, moveTo: IIPath.MoveToProc, lineTo: IIPath.LineToProc, curveTo: IIPath.CurveToProc] ~ { InitByteIndex: PROC RETURNS [INT] ~ TRUSTED { filePos: PrePressFontFormat.RelFilePos; filePosBytes: INT ~ Bytes[SIZE[PrePressFontFormat.RelFilePos]]; RawFetch[base: group.base, byteOffset: group.directoryByteOffset + (code-group.bc)*filePosBytes, destination: @filePos, nBytes: filePosBytes]; IF filePos=PrePressFontFormat.missingFilePos THEN ERROR MalformedCDFont; RETURN [group.directoryByteOffset + FileBytes[PrePressFontFormat.IntFromBcpl[filePos]]]; }; byteIndex: INT _ InitByteIndex[]; lp: VEC _ [0, 0]; GetCode: PROC RETURNS [PrePressFontFormat.SplineCode] ~ TRUSTED { sCmd: PrePressFontFormat.SplineCommand; nBytes: NAT ~ Bytes[SIZE[PrePressFontFormat.SplineCommand]]; RawFetch[base: group.base, byteOffset: byteIndex, destination: @sCmd, nBytes: nBytes]; byteIndex _ byteIndex + nBytes; RETURN[sCmd.code]; }; GetCoords: PROC RETURNS [VEC] ~ TRUSTED { sCo: PrePressFontFormat.SplineCoords; nBytes: NAT ~ Bytes[SIZE[PrePressFontFormat.SplineCoords]]; RawFetch[base: group.base, byteOffset: byteIndex, destination: @sCo, nBytes: nBytes]; byteIndex _ byteIndex + nBytes; RETURN[[RealConvert.BcplToIeee[sCo.x], RealConvert.BcplToIeee[sCo.y]]]; }; DO SELECT GetCode[] FROM $moveTo => { p: VEC ~ GetCoords[]; moveTo[p]; lp _ p; }; $drawTo => { p: VEC ~ GetCoords[]; lineTo[p]; lp _ p; }; $drawCurve => { c1: VEC ~ GetCoords[]; c2: VEC ~ GetCoords[]; c3: VEC ~ GetCoords[]; b1: VEC ~ lp.Add[c1.Div[3]]; b2: VEC ~ b1.Add[c1.Add[c2].Div[3]]; b3: VEC ~ lp.Add[c1].Add[c2].Add[c3]; curveTo[b1, b2, b3]; lp _ b3; }; $endDefinition => EXIT; $newObject => ERROR MalformedCDFont; -- not implemented ENDCASE => ERROR MalformedCDFont; -- undefined ENDLOOP; }; acClass: IITypeface.TypefaceClass ~ NEW [IITypeface.TypefaceClassRep _ [ type: $CD, Contains: CDContains, NextChar: CDNextChar, Escapement: CDEscapement, Amplified: CDAmplified, Correction: CDCorrection, BoundingBox: CDBoundingBox, FontBoundingBox: CDFontBoundingBox, Ligature: CDLigature, NextLigature: CDNextLigature, Kern: CDKern, NextKern: CDNextKern, Mask: CDMask ]]; IITypeface.Register["AC", CDCreate]; IITypeface.Register["CD", CDCreate]; IITypeface.Register["SD", CDCreate]; END.