RasterFontWriterImpl.mesa
Michael Plass, January 13, 1984 1:18 pm
DIRECTORY ImagerPixelMaps, Rope, RasterFontWriter, IO, FS, StrikeFormat, Basics, PressFontFormat, Real, VM;
RasterFontWriterImpl: CEDAR PROGRAM
IMPORTS ImagerPixelMaps, IO, FS, PressFontFormat, Rope, Real, VM
EXPORTS RasterFontWriter
~ BEGIN OPEN RasterFontWriter;
FormatError: PUBLIC ERROR [byteIndex: INT] ~ CODE;
Create: PUBLIC PROC [defaultBoxBounds: ImagerPixelMaps.DeviceRectangle, defaultWidth: INTEGER] RETURNS [internalFont: InternalFont] ~ {
defaultPixels: ImagerPixelMaps.PixelMap ← ImagerPixelMaps.Create[0, defaultBoxBounds];
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;
};
LoadAC: PROC [fileName: Rope.ROPE] RETURNS [internalFont: InternalFont] ~ TRUSTED {
openFile: FS.OpenFile ← FS.Open[fileName];
pages: INTVM.PagesForWords[FS.GetInfo[openFile].bytes];
vm: VM.Interval ← VM.Allocate[pages];
{ENABLE UNWIND => {VM.Free[vm]};
};
VM.Free[vm];
FormatError[0];
Not implemented yet.
};
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: ImagerPixelMaps.PixelMap;
header: StrikeFormat.Header;
internalFont ← NEW[InternalFontRep];
ReadBlock[@header, SIZE[StrikeFormat.Header], 0];
IF header.format.oneBit # T AND header.format.unused # 0 THEN {
IO.Close[file];
RETURN [LoadAC[fileName]];
};
IF header.format.oneBit # T THEN ERROR FormatError[0];
IF header.format.index # F THEN ERROR FormatError[0];
IF header.format.unused # 0 THEN ERROR FormatError[1];
IF header.format.kerned = T THEN {
boundingBox: StrikeFormat.BoundingBox;
body: StrikeFormat.Body;
xInSegment, prevXInSegment: CARDINAL;
xInSegmentOffset: INT;
widthEntryOffset: INT;
widthEntry: StrikeFormat.WidthEntry;
bodyOffset: INT;
ReadBlock[@boundingBox, SIZE[StrikeFormat.BoundingBox]];
bodyOffset ← CurWordOffset[];
ReadBlock[@body, SIZE[StrikeFormat.Body]];
strike ← ImagerPixelMaps.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 [header.min..header.max] DO
charPixels: ImagerPixelMaps.PixelMap ← strike;
ReadBlock[@xInSegment, SIZE[CARDINAL], xInSegmentOffset];
xInSegmentOffset ← xInSegmentOffset + SIZE[CARDINAL];
ReadBlock[@widthEntry, SIZE[StrikeFormat.WidthEntry], widthEntryOffset];
widthEntryOffset ← widthEntryOffset + SIZE[StrikeFormat.WidthEntry];
IF widthEntry # StrikeFormat.nullWidthEntry THEN {
IF widthEntry.width > header.maxwidth THEN ERROR FormatError[(widthEntryOffset-SIZE[StrikeFormat.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[StrikeFormat.WidthEntry], widthEntryOffset];
widthEntryOffset ← widthEntryOffset + SIZE[StrikeFormat.WidthEntry];
IF widthEntry # StrikeFormat.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: StrikeFormat.Body;
xInSegment, prevXInSegment: CARDINAL;
ReadBlock[@body, SIZE[StrikeFormat.Body]];
strike ← ImagerPixelMaps.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 [header.min..header.max] DO
charPixels: ImagerPixelMaps.PixelMap ← strike;
ReadBlock[@xInSegment, SIZE[CARDINAL]];
IF xInSegment>prevXInSegment THEN {
charPixels.fOrigin ← -prevXInSegment;
charPixels.fMin ← prevXInSegment;
charPixels.fSize ← xInSegment-prevXInSegment;
IF 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 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: ImagerPixelMaps.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: StrikeFormat.WidthEntry] ~ {
window: ImagerPixelMaps.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: StrikeFormat.Header;
boundingBox: StrikeFormat.BoundingBox ← [fbbox: fMin, fbboy: -sMax, fbbdx: fMax-fMin, fbbdy: sMax-sMin];
body: StrikeFormat.Body;
strike: ImagerPixelMaps.PixelMap;
widthEntry: StrikeFormat.WidthEntry;
f: INTEGER;
strike ← ImagerPixelMaps.Create[0, [-ascent, 0, ascent+descent, strikeWidth]];
f ← 0;
FOR c: CHAR IN [min..max] DO
IF internalFont.charRep[c] # internalFont.defaultChar THEN {
pixels: ImagerPixelMaps.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.format ← [oneBit: T, index: F, fixed: F, kerned: T, unused: 0];
header.min ← min;
header.max ← max;
header.maxwidth ← maxwidth;
body.length ← strike.refRep.words+SIZE[StrikeFormat.Body]+(max-min+3)*SIZE[CARDINAL];
body.ascent ← ascent;
body.descent ← descent;
body.xoffset ← 0;
body.raster ← strike.refRep.rast;
WriteBlock[@header, SIZE[StrikeFormat.Header]];
WriteBlock[@boundingBox, SIZE[StrikeFormat.BoundingBox]];
bodyStart ← file.GetIndex;
WriteBlock[@body, SIZE[StrikeFormat.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 ← StrikeFormat.nullWidthEntry
ELSE ComputeWidthEntry[internalFont.charRep[c], fMin];
WriteBlock[@widthEntry, SIZE[StrikeFormat.WidthEntry]];
ENDLOOP;
widthEntry ← ComputeWidthEntry[internalFont.defaultChar, fMin];
WriteBlock[@widthEntry, SIZE[StrikeFormat.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: StrikeFormat.Header;
strike: ImagerPixelMaps.PixelMap;
body: StrikeFormat.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 ← ImagerPixelMaps.Create[0, [-ascent, 0, ascent+descent, strikeWidth]];
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.format ← [oneBit: T, index: F, fixed: F, kerned: F, unused: 0];
header.min ← min;
header.max ← max;
header.maxwidth ← maxwidth;
body.length ← strike.refRep.words+SIZE[StrikeFormat.Body]+(max-min+1+2)*SIZE[CARDINAL];
body.ascent ← ascent;
body.descent ← descent;
body.xoffset ← 0;
body.raster ← strike.refRep.rast;
WriteBlock[@header, SIZE[StrikeFormat.Header]];
bodyStart ← file.GetIndex;
WriteBlock[@body, SIZE[StrikeFormat.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: PressFontFormat.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: PressFontFormat.IndexHeader ← [type: name, length: 12];
WriteBlock[@hdr, SIZE[PressFontFormat.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, bitsPerInch: REAL ← 384.0] ~ TRUSTED {
micaSize: REAL ← internalFont.bitsPerEmQuad/bitsPerInch*2540.0;
bitsPerInchX: REAL ← bitsPerInch;
bitsPerInchY: REAL ← bitsPerInch;
file: IO.STREAM ← FS.StreamOpen[fileName, $create];
WriteBlock: UNSAFE PROC [source: LONG POINTER, words: CARDINAL] ~ UNCHECKED {
file.UnsafePutBlock[[source, 0, words*Basics.bytesPerWord]];
};
CurWordOffset: PROC RETURNS [wordOffset: INT] ~ CHECKED {
wordOffset ← file.GetIndex/Basics.bytesPerWord;
};
min, max: CHAR;
endIndexEntry: PressFontFormat.IndexHeader ← [type: end, length: SIZE[PressFontFormat.IndexHeader]];
charsIndexEntryByteLoc: INT;
charsIndexEntry: PressFontFormat.RawIndex.chars;
pixelMap: ImagerPixelMaps.PixelMap ← ImagerPixelMaps.Create[0, [0,0,16,16]];
[min, max, ----, ----, ----, ----, ----, ----, ----] ← ComputeFontMetrics[internalFont];
charsIndexEntry ← [
hdr: [type: chars, length: SIZE[PressFontFormat.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[PressFontFormat.IndexHeader]];
IF max>=min THEN {
startaddress: INT ← CurWordOffset[];
directoryStart, filePos: INT;
missingCharData: PressFontFormat.BoundingBox ← [xwidth: [0, 0], ywidth: [0, 0], BBox: 0, BBoy: 0, BBdx: 0, BBdy: LAST[CARDINAL]];
missingCharDirectoryMarker: PressFontFormat.bcplLONGCARDINAL ← [LAST[CARDINAL], LAST[CARDINAL]];
FOR c: CHAR IN [min..max] DO
IF internalFont.charRep[c] = internalFont.defaultChar THEN {
WriteBlock[@missingCharData, SIZE[PressFontFormat.BoundingBox]];
}
ELSE {
charRep: InternalCharRep ~ internalFont.charRep[c];
window: ImagerPixelMaps.DeviceRectangle ← charRep.pixels.Window;
xwidth: PressFontFormat.Fraction ~ Real.RoundLI[charRep.fWidth * 65536];
ywidth: PressFontFormat.Fraction ~ Real.RoundLI[charRep.sWidth * 65536];
charData: PressFontFormat.BoundingBox ← [
xwidth: PressFontFormat.MesaToBcplFraction[xwidth],
ywidth: PressFontFormat.MesaToBcplFraction[ywidth],
BBox: window.fMin,
BBoy: -window.sSize-window.sMin,
BBdx: window.fSize,
BBdy: window.sSize
];
WriteBlock[@charData, SIZE[PressFontFormat.BoundingBox]];
};
ENDLOOP;
directoryStart ← CurWordOffset[];
filePos ← directoryStart + (max-min+1)*SIZE[PressFontFormat.bcplLONGCARDINAL];
FOR c: CHAR IN [min..max] DO
charRep: InternalCharRep ~ internalFont.charRep[c];
IF charRep = internalFont.defaultChar THEN {
WriteBlock[@missingCharDirectoryMarker, SIZE[PressFontFormat.bcplLONGCARDINAL]];
}
ELSE {
relFilePos: PressFontFormat.bcplLONGCARDINAL ← PressFontFormat.MesaToBcplLongCardinal[filePos-directoryStart];
bbdx: NAT ~ charRep.pixels.fSize;
bbdy: NAT ~ charRep.pixels.sSize;
bbdyW: NAT ~ (bbdy+Basics.bitsPerWord-1)/Basics.bitsPerWord;
WriteBlock[@relFilePos, SIZE[PressFontFormat.bcplLONGCARDINAL]];
filePos ← filePos + 1 + bbdyW*bbdx;
};
ENDLOOP;
IF CurWordOffset[] # directoryStart + (max-min+1)*SIZE[PressFontFormat.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 ← ImagerPixelMaps.Rotate[charRep.pixels, pixelMap.refRep];
IF pixelMap.refRep.lines # bbdx THEN ERROR;
IF pixelMap.refRep.rast # bbdyW THEN ERROR;
WriteBlock[pixelMap.refRep.pointer, bbdyW*bbdx];
};
ENDLOOP;
IF filePos # CurWordOffset[] THEN ERROR;
charsIndexEntry.startaddress ← LOOPHOLE[PressFontFormat.MesaToBcplLongCardinal[startaddress]];
charsIndexEntry.length ← PressFontFormat.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.