RasterFontIOImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, May 20, 1985 4:19:36 pm PDT
Last changed by Pavel on October 12, 1984 6:21:43 pm PDT
Removed some dependencies upon glyphs being small enough for 16-bit numbers.
DIRECTORY
Basics USING [bitsPerWord, bytesPerWord, LongMult],
CountedVM USING [Allocate, Handle],
FEPressFontFormat USING [bcplLONGCARDINAL, BoundingBox, FontCode, Fraction, IndexHeader, MesaToBcplFraction, MesaToBcplLongCardinal, RawIndex],
FS USING [StreamOpen],
ImagerPixelMap USING [Clear, Create, CreateFrameBuffer, DeviceRectangle, Fill, PixelMap, Reflect, Rotate, ShiftMap, Transfer, Trim, Window],
IO USING [Close, GetIndex, GetLength, PutChar, PutRope, SetIndex, STREAM, UnsafeGetBlock, UnsafePutBlock],
PrePressFontFormat USING [CardFromBcpl, CharacterData, CharacterIndexEntry, CharDataArray, DirectoryArray, IntFromBcpl, IXHeader, missingCharacter, missingFilePos, NameIndexEntry, RasterDefn],
RasterFontIO USING [InternalCharRep, InternalFont, InternalFontRep],
Real USING [FScale, RoundLI],
Rope USING [FromProc, Length, ROPE, Substr],
StrikeFontFormat USING [Body, BoundingBox, Header, nullWidthEntry, WidthEntry];
RasterFontIOImpl: CEDAR PROGRAM
IMPORTS FS, CountedVM, ImagerPixelMap, IO, Real, Rope, PrePressFontFormat, FEPressFontFormat
EXPORTS RasterFontIO ~
BEGIN
OPEN RasterFontIO;
ROPE: TYPE ~ Rope.ROPE;
FormatError: PUBLIC ERROR [byteIndex: INT] ~ CODE;
Create: PUBLIC PROC [defaultBoxBounds: ImagerPixelMap.DeviceRectangle, defaultWidth: INTEGER] RETURNS [internalFont: InternalFont] ~ {
defaultPixels: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[0, defaultBoxBounds];
defaultPixels.Clear;
defaultPixels.Fill[defaultBoxBounds, 1];
internalFont ← NEW [InternalFontRep];
internalFont.defaultChar ← [fWidth: defaultWidth, sWidth: 0, pixels: defaultPixels];
FOR char: CHAR IN CHAR DO
internalFont.charRep[char] ← internalFont.defaultChar;
ENDLOOP;
};
SetWordIndex: PROC[stream: IO.STREAM, wordIndex: INT] ~ {
index: CARDINAL ~ wordIndex*Basics.bytesPerWord;
IO.SetIndex[stream, index];
};
ReadWords: UNSAFE PROC[stream: IO.STREAM, base: LONG POINTER, words: INT] ~ UNCHECKED {
count: INT ~ words*Basics.bytesPerWord;
IF IO.UnsafeGetBlock[stream, [base: base, count: count]]=count THEN NULL
ELSE ERROR FormatError[IO.GetIndex[stream]]; -- file too short
};
missingOffset: INT ~
PrePressFontFormat.IntFromBcpl[PrePressFontFormat.missingFilePos];
LoadAC: PROC [fileName: ROPE] RETURNS [internalFont: InternalFont] ~ {
stream: IO.STREAM ~ FS.StreamOpen[fileName];
Bad: PROC [backoff: INT] ~ {ERROR FormatError[IO.GetIndex[stream]-backoff]};
ix: PrePressFontFormat.IXHeader;
ixSize: NAT ~ SIZE[PrePressFontFormat.IXHeader];
name: PrePressFontFormat.NameIndexEntry;
index: PrePressFontFormat.CharacterIndexEntry;
nameFound, indexFound: BOOLFALSE;
family: ROPENIL;
segmentIndex, segmentWords: INT ← 0;
vm: CountedVM.Handle ← NIL;
charData: LONG POINTER TO PrePressFontFormat.CharDataArray ← NIL;
directory: LONG POINTER TO PrePressFontFormat.DirectoryArray ← NIL;
bitsPerMica: REAL ← 1.0;
DO -- read the index part
TRUSTED { ReadWords[stream, @ix, SIZE[PrePressFontFormat.IXHeader]] };
SELECT ix.type FROM
end => EXIT;
name => {
IF nameFound THEN Bad[SIZE[PrePressFontFormat.IXHeader]]; -- more than one name entry
IF (ix.length-ixSize)=SIZE[PrePressFontFormat.NameIndexEntry] THEN TRUSTED {
ReadWords[stream, @name, SIZE[PrePressFontFormat.NameIndexEntry]] }
ELSE Bad[SIZE[PrePressFontFormat.IXHeader]]; -- wrong ix.length
{ -- convert name to rope
i: NAT ← 0; p: PROC RETURNS[CHAR] ~ { RETURN[VAL[name.chars[i ← i+1]]] };
family ← Rope.FromProc[len: name.chars[0], p: p];
};
nameFound ← TRUE;
};
character => {
IF indexFound THEN Bad[SIZE[PrePressFontFormat.IXHeader]]; -- more than one char index entry
IF (ix.length-ixSize)=SIZE[PrePressFontFormat.CharacterIndexEntry] THEN TRUSTED {
ReadWords[stream, @index, SIZE[PrePressFontFormat.CharacterIndexEntry]] }
ELSE Bad[SIZE[PrePressFontFormat.IXHeader]]; -- wrong ix.length
indexFound ← TRUE;
};
ENDCASE => Bad[SIZE[PrePressFontFormat.IXHeader]]; -- unexpected ix type
ENDLOOP;
IF nameFound AND indexFound AND name.code=index.family THEN NULL
ELSE Bad[0]; -- index part has wrong form
IF index.bc>index.ec THEN Bad[0]; -- bc exceeds ec
segmentIndex ← PrePressFontFormat.CardFromBcpl[index.segmentSA]; -- in words!
segmentWords ← PrePressFontFormat.CardFromBcpl[index.segmentLength];
vm ← CountedVM.Allocate[words: segmentWords];
SetWordIndex[stream, segmentIndex];
TRUSTED { -- read segment
base: LONG POINTER ~ vm.pointer;
ReadWords[stream, base, segmentWords];
charData ← base;
directory ← base+SIZE[PrePressFontFormat.CharDataArray[index.ec-index.bc+1]];
};
IO.Close[stream];
internalFont ← Create[defaultBoxBounds: [-8, 1, 8, 8], defaultWidth: 10];
internalFont.family ← family;
internalFont.face ← index.face;
internalFont.bitsPerInch ← index.resolutionX/10.0;
bitsPerMica ← internalFont.bitsPerInch/2540.0;
internalFont.bitsPerEmQuad ← index.size*bitsPerMica;
FOR c: CHAR IN [VAL[index.bc]..VAL[index.ec]] DO
charIndex: NAT ~ ORD[c]-index.bc;
GetDefn: PROC RETURNS [LONG POINTER TO PrePressFontFormat.RasterDefn] ~ TRUSTED {
fp: INT ~ PrePressFontFormat.IntFromBcpl[directory[charIndex]];
IF fp#missingOffset THEN RETURN [LOOPHOLE[directory+fp]]
ELSE FormatError[0];
};
cd: PrePressFontFormat.CharacterData;
TRUSTED { cd ← charData[charIndex] };
IF cd.bbdy#PrePressFontFormat.missingCharacter THEN TRUSTED {
defn: LONG POINTER TO PrePressFontFormat.RasterDefn ~ GetDefn[];
raster: NAT ~ defn^.raster;
lines: NAT ~ defn^.lines;
IF lines=cd.bbdx AND raster=(cd.bbdy+15)/16 THEN TRUSTED {
base: LONG POINTER ~ defn+SIZE[PrePressFontFormat.RasterDefn];
pixelsA: ImagerPixelMap.PixelMap ← ImagerPixelMap.CreateFrameBuffer[pointer: base, words: Basics.LongMult[raster, lines], lgBitsPerPixel: 0, rast: raster, lines: lines, ref: vm].ShiftMap[cd.bbox, cd.bboy];
pixelsB: ImagerPixelMap.PixelMap ← pixelsA.Reflect;
pixelsA ← pixelsB.Rotate[scratch: pixelsA.refRep];
pixelsB ← pixelsA.Reflect[scratch: pixelsB.refRep];
internalFont.charRep[c] ← [
fWidth: Real.FScale[PrePressFontFormat.IntFromBcpl[cd.wx], -16],
sWidth: -Real.FScale[PrePressFontFormat.IntFromBcpl[cd.wy], -16],
pixels: pixelsB
];
}
ELSE Bad[0]; -- raster and bounding box inconsistent
};
ENDLOOP;
};
paranoid: BOOLTRUE;
enables more stringent error checking.
Load: PUBLIC PROC [fileName: Rope.ROPE] RETURNS [internalFont: InternalFont] ~ TRUSTED {
file: IO.STREAM ← FS.StreamOpen[fileName];
ReadBlock: UNSAFE PROC [dest: LONG POINTER, words: CARDINAL, wordOffset: INT ← -1] ~ UNCHECKED {
IF wordOffset >= 0 THEN file.SetIndex[wordOffset*Basics.bytesPerWord];
[] ← file.UnsafeGetBlock[[dest, 0, words*Basics.bytesPerWord]];
};
CurWordOffset: PROC RETURNS [wordOffset: INT] ~ CHECKED {
wordOffset ← file.GetIndex/Basics.bytesPerWord;
};
strike: ImagerPixelMap.PixelMap;
header: StrikeFontFormat.Header;
internalFont ← NEW[InternalFontRep];
ReadBlock[@header, SIZE[StrikeFontFormat.Header], 0];
IF header.oneBit # T AND header.unused # 0 THEN {
IO.Close[file];
RETURN [LoadAC[fileName]];
};
IF header.oneBit # T THEN ERROR FormatError[0];
IF header.index # F THEN ERROR FormatError[0];
IF header.unused # 0 THEN ERROR FormatError[1];
IF header.kerned = T THEN {
boundingBox: StrikeFontFormat.BoundingBox;
body: StrikeFontFormat.Body;
xInSegment, prevXInSegment: CARDINAL;
xInSegmentOffset: INT;
widthEntryOffset: INT;
widthEntry: StrikeFontFormat.WidthEntry;
bodyOffset: INT;
ReadBlock[@boundingBox, SIZE[StrikeFontFormat.BoundingBox]];
bodyOffset ← CurWordOffset[];
ReadBlock[@body, SIZE[StrikeFontFormat.Body]];
strike ← ImagerPixelMap.Create[0, [-body.ascent, 0, body.ascent+body.descent, body.raster*Basics.bitsPerWord]];
ReadBlock[strike.refRep.pointer, strike.refRep.words];
widthEntryOffset ← CurWordOffset[] + (header.max-header.min+3)*SIZE[CARDINAL];
IF widthEntryOffset # bodyOffset + body.length THEN ERROR FormatError[bodyOffset*SIZE[CARDINAL]];
ReadBlock[@prevXInSegment, SIZE[CARDINAL]];
xInSegmentOffset ← CurWordOffset[];
FOR char: CHAR IN [VAL[header.min]..VAL[header.max]] DO
charPixels: ImagerPixelMap.PixelMap ← strike;
ReadBlock[@xInSegment, SIZE[CARDINAL], xInSegmentOffset];
xInSegmentOffset ← xInSegmentOffset + SIZE[CARDINAL];
ReadBlock[@widthEntry, SIZE[StrikeFontFormat.WidthEntry], widthEntryOffset];
widthEntryOffset ← widthEntryOffset + SIZE[StrikeFontFormat.WidthEntry];
IF widthEntry # StrikeFontFormat.nullWidthEntry THEN {
IF widthEntry.width > header.maxwidth THEN ERROR FormatError[(widthEntryOffset-SIZE[StrikeFontFormat.WidthEntry])*Basics.bytesPerWord];
charPixels.fOrigin ← widthEntry.offset+boundingBox.fbbox-prevXInSegment;
charPixels.fMin ← prevXInSegment;
charPixels.fSize ← xInSegment-prevXInSegment;
internalFont.charRep[char] ← [
sWidth: 0,
fWidth: widthEntry.width,
pixels: charPixels
];
};
prevXInSegment ← xInSegment;
ENDLOOP;
ReadBlock[@xInSegment, SIZE[CARDINAL], xInSegmentOffset];
xInSegmentOffset ← xInSegmentOffset + SIZE[CARDINAL];
ReadBlock[@widthEntry, SIZE[StrikeFontFormat.WidthEntry], widthEntryOffset];
widthEntryOffset ← widthEntryOffset + SIZE[StrikeFontFormat.WidthEntry];
IF widthEntry # StrikeFontFormat.nullWidthEntry THEN {
strike.fOrigin ← widthEntry.offset+boundingBox.fbbox-prevXInSegment;
strike.fMin ← prevXInSegment;
strike.fSize ← xInSegment-prevXInSegment;
internalFont.defaultChar ← [
sWidth: 0,
fWidth: widthEntry.width,
pixels: strike
];
}
ELSE ERROR FormatError[file.GetIndex];
}
ELSE {
body: StrikeFontFormat.Body;
xInSegment, prevXInSegment: CARDINAL;
ReadBlock[@body, SIZE[StrikeFontFormat.Body]];
strike ← ImagerPixelMap.Create[0, [-body.ascent, 0, body.ascent+body.descent, body.raster*Basics.bitsPerWord]];
ReadBlock[strike.refRep.pointer, strike.refRep.words];
ReadBlock[@prevXInSegment, SIZE[CARDINAL]];
FOR char: CHAR IN [VAL[header.min]..VAL[header.max]] DO
charPixels: ImagerPixelMap.PixelMap ← strike;
ReadBlock[@xInSegment, SIZE[CARDINAL]];
IF xInSegment>prevXInSegment THEN {
charPixels.fOrigin ← -prevXInSegment;
charPixels.fMin ← prevXInSegment;
charPixels.fSize ← xInSegment-prevXInSegment;
IF paranoid AND charPixels.fSize > header.maxwidth THEN ERROR FormatError[file.GetIndex-2];
internalFont.charRep[char] ← [
sWidth: 0,
fWidth: charPixels.fSize,
pixels: charPixels
];
};
prevXInSegment ← xInSegment;
ENDLOOP;
ReadBlock[@xInSegment, SIZE[CARDINAL]];
strike.fOrigin ← -prevXInSegment;
strike.fMin ← prevXInSegment;
strike.fSize ← xInSegment-prevXInSegment;
internalFont.defaultChar ← [
sWidth: 0,
fWidth: strike.fSize,
pixels: strike
];
};
FOR char: CHAR IN CHAR DO
IF internalFont.charRep[char].pixels.refRep = NIL THEN {
internalFont.charRep[char] ← internalFont.defaultChar;
};
ENDLOOP;
IF paranoid AND file.GetIndex # file.GetLength THEN ERROR FormatError[file.GetIndex];
file.Close;
};
ComputeFontMetrics: PUBLIC PROC [internalFont: InternalFont] RETURNS [bc, ec: CHAR, sMin, fMin, sMax, fMax: INTEGER, maxWidth, totalWidth, fSizeStrike: CARDINAL] ~ {
ProcessChar: PROC [charRep: InternalCharRep] ~ {
bb: ImagerPixelMap.DeviceRectangle ← charRep.pixels.Window;
fWidth: INT ← Real.RoundLI[charRep.fWidth];
IF bb.sSize > 0 AND bb.fSize > 0 THEN {
sMin ← MIN[sMin, bb.sMin];
fMin ← MIN[fMin, bb.fMin];
sMax ← MAX[sMax, bb.sMin+bb.sSize];
fMax ← MAX[fMax, bb.fMin+bb.fSize];
fSizeStrike ← fSizeStrike + bb.fSize;
};
totalWidth ← totalWidth + fWidth;
maxWidth ← MAX[fWidth, maxWidth];
};
bc ← '\000;
ec ← '\377;
WHILE bc < '\377 AND internalFont.charRep[bc] = internalFont.defaultChar DO bc ← bc + 1 ENDLOOP;
WHILE ec > '\000 AND internalFont.charRep[ec] = internalFont.defaultChar DO ec ← ec - 1 ENDLOOP;
maxWidth ← totalWidth← fSizeStrike ← 0;
sMin ← fMin ← sMax ← fMax ← 0;
FOR char: CHAR IN [bc..ec] DO
IF internalFont.charRep[char] # internalFont.defaultChar THEN ProcessChar[internalFont.charRep[char]];
ENDLOOP;
ProcessChar[internalFont.defaultChar];
};
ComputeWidthEntry: PROC [charRep: InternalCharRep, fMinFont: INTEGER] RETURNS [widthEntry: StrikeFontFormat.WidthEntry] ~ {
window: ImagerPixelMap.DeviceRectangle ← charRep.pixels.Window;
IF window.sSize = 0 OR window.fSize = 0 THEN window.fMin ← 0;
IF window.fMin-fMinFont < 0 THEN ERROR;
widthEntry.offset ← window.fMin-fMinFont;
widthEntry.width ← Real.RoundLI[charRep.fWidth];
};
WriteKernedStrike: PUBLIC PROC [internalFont: InternalFont, fileName: Rope.ROPE] ~ TRUSTED {
file: IO.STREAM ← FS.StreamOpen[fileName, $create];
WriteBlock: UNSAFE PROC [source: LONG POINTER, words: CARDINAL] ~ UNCHECKED {
file.UnsafePutBlock[[source, 0, words*Basics.bytesPerWord]];
};
min, max: CHAR;
sMin, fMin, sMax, fMax: INTEGER;
maxwidth, strikeWidth: CARDINAL;
[min, max, sMin, fMin, sMax, fMax, maxwidth, ----, strikeWidth] ← ComputeFontMetrics[internalFont];
IF max>=min THEN {
ascent: NAT ← -sMin;
descent: NAT ← sMax;
bodyStart: INT;
header: StrikeFontFormat.Header;
boundingBox: StrikeFontFormat.BoundingBox ← [fbbox: fMin, fbboy: -sMax, fbbdx: fMax-fMin, fbbdy: sMax-sMin];
body: StrikeFontFormat.Body;
strike: ImagerPixelMap.PixelMap;
widthEntry: StrikeFontFormat.WidthEntry;
f: INTEGER;
strike ← ImagerPixelMap.Create[0, [-ascent, 0, ascent+descent, strikeWidth]];
strike.Clear;
f ← 0;
FOR c: CHAR IN [min..max] DO
IF internalFont.charRep[c] # internalFont.defaultChar THEN {
pixels: ImagerPixelMap.PixelMap ← internalFont.charRep[c].pixels;
pixels ← pixels.ShiftMap[0, f-pixels.Window.fMin];
strike.Transfer[pixels];
IF pixels.sSize # 0 THEN f ← f + pixels.fSize;
};
ENDLOOP;
strike.Transfer[internalFont.defaultChar.pixels.ShiftMap[0, f-internalFont.defaultChar.pixels.Window.fMin]];
header.oneBit ← T;
header.index ← F;
header.fixed ← F;
header.kerned ← T;
header.unused ← 0;
header.min ← ORD[min];
header.max ← ORD[max];
header.maxwidth ← maxwidth;
body.length ← strike.refRep.words+SIZE[StrikeFontFormat.Body]+(max-min+3)*SIZE[CARDINAL];
body.ascent ← ascent;
body.descent ← descent;
body.xoffset ← 0;
body.raster ← strike.refRep.rast;
WriteBlock[@header, SIZE[StrikeFontFormat.Header]];
WriteBlock[@boundingBox, SIZE[StrikeFontFormat.BoundingBox]];
bodyStart ← file.GetIndex;
WriteBlock[@body, SIZE[StrikeFontFormat.Body]];
WriteBlock[strike.refRep.pointer, strike.refRep.words];
strikeWidth ← 0;
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
FOR c: CHAR IN [min..max] DO
charRep: InternalCharRep ← internalFont.charRep[c];
IF charRep # internalFont.defaultChar THEN {
IF charRep.pixels.sSize # 0 THEN
strikeWidth ← strikeWidth + charRep.pixels.fSize;
};
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
ENDLOOP;
IF internalFont.defaultChar.pixels.sSize # 0 THEN
strikeWidth ← strikeWidth + internalFont.defaultChar.pixels.fSize;
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
IF file.GetIndex-bodyStart # body.length*Basics.bytesPerWord THEN ERROR;
FOR c: CHAR IN [min..max] DO
widthEntry ← IF internalFont.charRep[c] = internalFont.defaultChar
THEN widthEntry ← StrikeFontFormat.nullWidthEntry
ELSE ComputeWidthEntry[internalFont.charRep[c], fMin];
WriteBlock[@widthEntry, SIZE[StrikeFontFormat.WidthEntry]];
ENDLOOP;
widthEntry ← ComputeWidthEntry[internalFont.defaultChar, fMin];
WriteBlock[@widthEntry, SIZE[StrikeFontFormat.WidthEntry]];
};
file.Close;
};
WriteStrike: PUBLIC PROC [internalFont: InternalFont, fileName: Rope.ROPE] ~ TRUSTED {
file: IO.STREAM ← FS.StreamOpen[fileName, $create];
WriteBlock: UNSAFE PROC [source: LONG POINTER, words: CARDINAL] ~ UNCHECKED {
file.UnsafePutBlock[[source, 0, words*Basics.bytesPerWord]];
};
header: StrikeFontFormat.Header;
strike: ImagerPixelMap.PixelMap;
body: StrikeFontFormat.Body;
min, max: CHAR;
sMin, fMin, sMax, fMax: INTEGER;
maxwidth, strikeWidth: CARDINAL;
[min, max, sMin, fMin, sMax, fMax, maxwidth, strikeWidth, ----] ← ComputeFontMetrics[internalFont];
IF max>=min THEN {
ascent: NAT ← -sMin;
descent: NAT ← sMax;
bodyStart: INT;
strike ← ImagerPixelMap.Create[0, [-ascent, 0, ascent+descent, strikeWidth]];
strike.Clear;
strikeWidth ← 0;
FOR c: CHAR IN [min..max] DO
IF internalFont.charRep[c] # internalFont.defaultChar THEN {
strike.Transfer[internalFont.charRep[c].pixels.ShiftMap[0, strikeWidth]];
strikeWidth ← strikeWidth + Real.RoundLI[internalFont.charRep[c].fWidth];
};
ENDLOOP;
strike.Transfer[internalFont.defaultChar.pixels.ShiftMap[0, strikeWidth]];
header.oneBit ← T;
header.index ← F;
header.fixed ← F;
header.kerned ← F;
header.unused ← 0;
header.min ← ORD[min];
header.max ← ORD[max];
header.maxwidth ← maxwidth;
body.length ← strike.refRep.words+SIZE[StrikeFontFormat.Body]+(max-min+1+2)*SIZE[CARDINAL];
body.ascent ← ascent;
body.descent ← descent;
body.xoffset ← 0;
body.raster ← strike.refRep.rast;
WriteBlock[@header, SIZE[StrikeFontFormat.Header]];
bodyStart ← file.GetIndex;
WriteBlock[@body, SIZE[StrikeFontFormat.Body]];
WriteBlock[strike.refRep.pointer, strike.refRep.words];
strikeWidth ← 0;
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
FOR c: CHAR IN [min..max] DO
IF internalFont.charRep[c] # internalFont.defaultChar THEN {
strikeWidth ← strikeWidth + Real.RoundLI[internalFont.charRep[c].fWidth];
};
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
ENDLOOP;
strikeWidth ← strikeWidth + Real.RoundLI[internalFont.defaultChar.fWidth];
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
IF file.GetIndex-bodyStart # body.length*Basics.bytesPerWord THEN ERROR;
};
file.Close;
};
WriteNameEntry: PROC [file: IO.STREAM, code: FEPressFontFormat.FontCode, name: Rope.ROPE] ~ TRUSTED {
WriteBlock: UNSAFE PROC [source: LONG POINTER, words: CARDINAL] ~ UNCHECKED {
file.UnsafePutBlock[[source, 0, words*Basics.bytesPerWord]];
};
shortenedName: Rope.ROPE ← name.Substr[len: 19];
length: NAT ← shortenedName.Length;
hdr: FEPressFontFormat.IndexHeader ← [type: name, length: 12];
WriteBlock[@hdr, SIZE[FEPressFontFormat.IndexHeader]];
file.PutChar['\000];
file.PutChar['\000+code];
file.PutChar['\000+length];
file.PutRope[shortenedName];
WHILE length < 19 DO file.PutChar['\000]; length ← length + 1 ENDLOOP;
};
WriteAC: PUBLIC PROC [internalFont: InternalFont, fileName: Rope.ROPE] ~ TRUSTED {
micaSize: REAL ← internalFont.bitsPerEmQuad/internalFont.bitsPerInch*2540.0;
bitsPerInchX: REAL ← internalFont.bitsPerInch;
bitsPerInchY: REAL ← internalFont.bitsPerInch;
file: IO.STREAM ← FS.StreamOpen[fileName, $create];
WriteBlock: UNSAFE PROC [source: LONG POINTER, words: INT] ~ UNCHECKED {
file.UnsafePutBlock[[source, 0, words*Basics.bytesPerWord]];
};
CurWordOffset: PROC RETURNS [wordOffset: INT] ~ CHECKED {
wordOffset ← file.GetIndex/Basics.bytesPerWord;
};
min, max: CHAR;
endIndexEntry: FEPressFontFormat.IndexHeader ← [type: end, length: SIZE[FEPressFontFormat.IndexHeader]];
charsIndexEntryByteLoc: INT;
charsIndexEntry: FEPressFontFormat.RawIndex.chars;
pixelMap: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[0, [0,0,16,16]];
pixelMap.Clear;
[min, max, ----, ----, ----, ----, ----, ----, ----] ← ComputeFontMetrics[internalFont];
charsIndexEntry ← [
hdr: [type: chars, length: SIZE[FEPressFontFormat.RawIndex.chars]],
variantPart: chars[
family: 0,
face: internalFont.face,
bc: min,
ec: max,
size: Real.RoundLI[micaSize],
rotation: 0,
startaddress: [0,0],
length: [0,0],
resolutionx: Real.RoundLI[bitsPerInchX*10],
resolutiony: Real.RoundLI[bitsPerInchY*10]
]
];
WriteNameEntry[file, 0, internalFont.family];
charsIndexEntryByteLoc ← file.GetIndex;
IF max>=min THEN {
WriteBlock[@charsIndexEntry, charsIndexEntry.hdr.length];
};
WriteBlock[@endIndexEntry, SIZE[FEPressFontFormat.IndexHeader]];
IF max>=min THEN {
startaddress: INT ← CurWordOffset[];
directoryStart, filePos: INT;
missingCharData: FEPressFontFormat.BoundingBox ← [xwidth: [0, 0], ywidth: [0, 0], BBox: 0, BBoy: 0, BBdx: 0, BBdy: LAST[CARDINAL]];
missingCharDirectoryMarker: FEPressFontFormat.bcplLONGCARDINAL ← [LAST[CARDINAL], LAST[CARDINAL]];
FOR c: CHAR IN [min..max] DO
IF internalFont.charRep[c] = internalFont.defaultChar THEN {
WriteBlock[@missingCharData, SIZE[FEPressFontFormat.BoundingBox]];
}
ELSE {
charRep: InternalCharRep ~ internalFont.charRep[c];
window: ImagerPixelMap.DeviceRectangle ← charRep.pixels.Window;
xwidth: FEPressFontFormat.Fraction ~ Real.RoundLI[charRep.fWidth * 65536];
ywidth: FEPressFontFormat.Fraction ~ Real.RoundLI[-charRep.sWidth * 65536];
charData: FEPressFontFormat.BoundingBox ← [
xwidth: FEPressFontFormat.MesaToBcplFraction[xwidth],
ywidth: FEPressFontFormat.MesaToBcplFraction[ywidth],
BBox: window.fMin,
BBoy: -window.sSize-window.sMin,
BBdx: window.fSize,
BBdy: window.sSize
];
WriteBlock[@charData, SIZE[FEPressFontFormat.BoundingBox]];
};
ENDLOOP;
directoryStart ← CurWordOffset[];
filePos ← directoryStart + (max-min+1)*SIZE[FEPressFontFormat.bcplLONGCARDINAL];
FOR c: CHAR IN [min..max] DO
charRep: InternalCharRep ~ internalFont.charRep[c];
IF charRep = internalFont.defaultChar THEN {
WriteBlock[@missingCharDirectoryMarker, SIZE[FEPressFontFormat.bcplLONGCARDINAL]];
}
ELSE {
relFilePos: FEPressFontFormat.bcplLONGCARDINAL ← FEPressFontFormat.MesaToBcplLongCardinal[filePos-directoryStart];
bbdx: NAT ~ charRep.pixels.fSize;
bbdy: NAT ~ charRep.pixels.sSize;
bbdyW: NAT ~ (bbdy+Basics.bitsPerWord-1)/Basics.bitsPerWord;
WriteBlock[@relFilePos, SIZE[FEPressFontFormat.bcplLONGCARDINAL]];
filePos ← filePos + 1 + INT[bbdyW]*bbdx;
};
ENDLOOP;
IF CurWordOffset[] # directoryStart + (max-min+1)*SIZE[FEPressFontFormat.bcplLONGCARDINAL] THEN ERROR;
FOR c: CHAR IN [min..max] DO
charRep: InternalCharRep ~ internalFont.charRep[c];
IF charRep # internalFont.defaultChar THEN {
bbdx: NAT ~ charRep.pixels.fSize;
bbdy: NAT ~ charRep.pixels.sSize;
bbdyW: NAT ~ (bbdy+Basics.bitsPerWord-1)/Basics.bitsPerWord;
rasterDef: MACHINE DEPENDENT RECORD [bbdyW: [0..64), bbdy: [0..1024)] ← [bbdyW, bbdx];
WriteBlock[@rasterDef, 1];
pixelMap ← ImagerPixelMap.Rotate[charRep.pixels, pixelMap.refRep];
IF pixelMap.refRep.lines # bbdx THEN ERROR;
IF pixelMap.refRep.rast # bbdyW THEN ERROR;
WriteBlock[pixelMap.refRep.pointer, INT[bbdyW]*bbdx];
};
ENDLOOP;
IF filePos # CurWordOffset[] THEN ERROR;
charsIndexEntry.startaddress ← LOOPHOLE[FEPressFontFormat.MesaToBcplLongCardinal[startaddress]];
charsIndexEntry.length ← FEPressFontFormat.MesaToBcplLongCardinal[CurWordOffset[] - startaddress];
file.SetIndex[charsIndexEntryByteLoc];
WriteBlock[@charsIndexEntry, charsIndexEntry.hdr.length];
};
file.Close;
};
Trim: PUBLIC PROC [internalFont: InternalFont] ~ {
oldDefault: InternalCharRep ← internalFont.defaultChar;
internalFont.defaultChar.pixels ← internalFont.defaultChar.pixels.Trim[0];
FOR c: CHAR IN CHAR DO
IF internalFont.charRep[c] = oldDefault THEN internalFont.charRep[c] ← internalFont.defaultChar
ELSE internalFont.charRep[c].pixels ← internalFont.charRep[c].pixels.Trim[0];
ENDLOOP;
};
END.