RasterFontWriterImpl.mesa
Michael Plass, September 7, 1983 2:37 pm
DIRECTORY ImagerPixelMaps, Rope, RasterFontWriter, IO, FileIO, StrikeFormat, Environment;
RasterFontWriterImpl: CEDAR PROGRAM
IMPORTS ImagerPixelMaps, IO, FileIO
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;
};
Load: PUBLIC PROC [fileName: Rope.ROPE] RETURNS [internalFont: InternalFont] ~ TRUSTED {
file: IO.STREAM ← FileIO.Open[fileName];
ReadBlock: UNSAFE PROC [dest: LONG POINTER, words: CARDINAL, wordOffset: INT ← -1] ~ UNCHECKED {
IF wordOffset >= 0 THEN file.SetIndex[wordOffset*Environment.bytesPerWord];
[] ← file.UnsafeGetBlock[[dest, 0, words*Environment.bytesPerWord]];
};
CurWordOffset: PROC RETURNS [wordOffset: INT] ~ CHECKED {
wordOffset ← file.GetIndex/Environment.bytesPerWord;
};
strike: ImagerPixelMaps.PixelMap;
header: StrikeFormat.Header;
internalFont ← NEW[InternalFontRep];
ReadBlock[@header, SIZE[StrikeFormat.Header], 0];
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*Environment.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])*Environment.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*Environment.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;
};
widthLimit: INTLAST[CARDINAL];
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;
sMin ← MIN[sMin, bb.sMin];
fMin ← MIN[fMin, bb.fMin];
sMax ← MAX[sMax, bb.sMin+bb.sSize];
fMax ← MAX[fMax, bb.fMin+bb.fSize];
totalWidth ← totalWidth + charRep.fWidth;
fSizeStrike ← fSizeStrike + bb.fSize;
IF charRep.fWidth > widthLimit THEN ERROR;
maxWidth ← MAX[charRep.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 ← LAST[INTEGER];
sMax ← fMax ← FIRST[INTEGER];
FOR char: CHAR IN [bc..ec] DO
IF internalFont.charRep[char] # internalFont.defaultChar THEN ProcessChar[internalFont.charRep[char]];
ENDLOOP;
ProcessChar[internalFont.defaultChar];
};
WriteKernedStrike: PUBLIC PROC [internalFont: InternalFont, fileName: Rope.ROPE] ~ TRUSTED {
file: IO.STREAM ← FileIO.Open[fileName, overwrite];
WriteBlock: UNSAFE PROC [source: LONG POINTER, words: CARDINAL] ~ UNCHECKED {
file.UnsafePutBlock[[source, 0, words*Environment.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];
f ← f + pixels.fSize;
};
ENDLOOP;
strike.Transfer[internalFont.defaultChar.pixels.ShiftMap[0, f]];
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
IF internalFont.charRep[c] # internalFont.defaultChar THEN {
strikeWidth ← strikeWidth + internalFont.charRep[c].pixels.fSize;
};
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
ENDLOOP;
strikeWidth ← strikeWidth + internalFont.defaultChar.pixels.fSize;
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
IF file.GetIndex-bodyStart # body.length*Environment.bytesPerWord THEN ERROR;
FOR c: CHAR IN [min..max] DO
IF internalFont.charRep[c] = internalFont.defaultChar THEN {
widthEntry ← StrikeFormat.nullWidthEntry
}
ELSE {
widthEntry.offset ← internalFont.charRep[c].pixels.Window.fMin-fMin;
widthEntry.width ← internalFont.charRep[c].fWidth;
};
WriteBlock[@widthEntry, SIZE[StrikeFormat.WidthEntry]];
ENDLOOP;
widthEntry.offset ← internalFont.defaultChar.pixels.Window.fMin-fMin;
widthEntry.width ← internalFont.defaultChar.fWidth;
WriteBlock[@widthEntry, SIZE[StrikeFormat.WidthEntry]];
};
file.Close;
};
WriteStrike: PUBLIC PROC [internalFont: InternalFont, fileName: Rope.ROPE] ~ TRUSTED {
file: IO.STREAM ← FileIO.Open[fileName, overwrite];
WriteBlock: UNSAFE PROC [source: LONG POINTER, words: CARDINAL] ~ UNCHECKED {
file.UnsafePutBlock[[source, 0, words*Environment.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 + 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 + internalFont.charRep[c].fWidth;
};
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
ENDLOOP;
strikeWidth ← strikeWidth + internalFont.defaultChar.fWidth;
WriteBlock[@strikeWidth, SIZE[CARDINAL]];
IF file.GetIndex-bodyStart # body.length*Environment.bytesPerWord THEN ERROR;
};
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.