IICDTypefaceImpl.mesa
Copyright Ó 1986 by Xerox Corporation. All rights reserved.
Doug Wyatt, May 29, 1985 10:58:54 am PDT
Michael Plass, December 3, 1986 3:28:52 pm PST
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: BOOLFALSE,
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: ROPENIL,
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;
Dragon conversion note; the construct Bytes[SIZE[type]] should be changed to work properly for 32-bit machines. We can get by with this here because the press font formats are all based on 16-bit words.
FileBytes: PROC [fileWords: INT] RETURNS [INT] ~ --INLINE-- {RETURN [fileWords*2]};
FileBytes is used to convert a count of 16-bit words into bytes. This does not change even if the machine's word size is other than 16;
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: BOOLTRUE; -- 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: BOOLTRUE; -- 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: BOOLFALSE;
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];
IF indexFound THEN ERROR MalformedSDFont; -- more than one char index entry
IF (ix.length-ixSize)=SIZE[PrePressFontFormat.StdIndexEntry] THEN TRUSTED {
ReadWords[stream, @index, SIZE[PrePressFontFormat.StdIndexEntry]]
}
ELSE ERROR MalformedSDFont; -- wrong ix.length
indexFound ← TRUE;
};
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 BYTEALL[LAST[BYTE]];
setec: ARRAY CharSet OF BYTEALL[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]],
bitsPerLine: raster*16, -- this is really 16, not bitsPerWord!
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.