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];
BEGIN
OPEN RasterFontIO;
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: BOOL ← FALSE;
family: ROPE ← NIL;
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:
BOOL ←
TRUE;
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;
};