PressScreenImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Program to make AIS/press file of display
Last edited by: Rick Cattell on June 18, 1982 1:58 pm
Last edited by: Warren Teitelman on 6-Apr-82 12:02:06
Last edited by: Michael Plass on April 25, 1985 11:29:36 am PST
Last Edited by: Beach, October 31, 1983 11:46 am
Last Edited by: Wyatt, December 9, 1983 3:09 pm
Michael Plass, June 2, 1985 4:26:26 pm PDT
Spreitzer, February 4, 1986 3:43:45 pm PST
DIRECTORY
Carets USING [ResumeCarets, SuspendCarets],
AISFileFormat,
Convert,
Cursors,
FS,
Imager,
ImagerArtwork,
ImagerInterpress,
ImagerTerminal,
InterminalBackdoor,
IO,
MessageWindow,
PressScreen,
Real,
Rope,
SirPress,
Terminal,
ViewerClasses,
ViewerLocks,
ViewerSpecs,
ViewerOps;
PressScreenImpl: CEDAR MONITOR
IMPORTS Carets, Convert, Cursors, FS, Imager, ImagerArtwork, ImagerInterpress, ImagerTerminal, InterminalBackdoor, IO, MessageWindow, Real, Rope, SirPress, Terminal, ViewerLocks, ViewerOps
EXPORTS PressScreen
= { OPEN PressScreen;
ROPE: TYPE = Rope.ROPE;
extraWords: NAT ← 6; -- Half-screen prints are widened by this much.
PressScreen: PUBLIC PROC[pressFileName: Rope.ROPE, which: Side] = TRUSTED {
Old interface to convert part or all of the bit mapped screen into a press file.
screen: SirPress.PressHandle← SirPress.Create[
fileNameForHeaderPage: pressFileName,
outputStream: FS.StreamOpen[pressFileName, $create]
];
vt: Terminal.Virtual ← InterminalBackdoor.terminal;
frameBuffer: Terminal.FrameBuffer ← Terminal.GetBWFrameBuffer[vt];
widthInWords: INT ← frameBuffer.wordsPerLine;
halfWidthInWords: NAT ← widthInWords/2;
screenPtr: LONG POINTER ← frameBuffer.base;
define the screen rectangle
IF which=bothSides THEN {
screen.SetPageSize[height: 85, width: 110, unit: SirPress.in/10];
screen.BeginScannedRectangle[
x: 1, y: 1,
numberOfLines: frameBuffer.height,
dotsPerLine: widthInWords*16,
height: 8*4,
unit: SirPress.in/4]
}
ELSE
screen.BeginScannedRectangle[
x: 1, y: 1,
numberOfLines: frameBuffer.height,
dotsPerLine: (halfWidthInWords+extraWords)*16,
unit: SirPress.in/4];
call ShowLine for each line of screen (or half-line, if left side)
FOR i: INT IN [0..frameBuffer.height) DO
offset: LONG CARDINAL ← i*widthInWords;
IF which=rightSide THEN offset ← offset + halfWidthInWords - extraWords;
screen.UnsafeShowLine[screenPtr + offset];
ENDLOOP;
finish the rectangle and close the file
screen.EndScannedRectangle[];
screen.ClosePress[];
};
fileNameTemplate: ROPE ← "Screen#.press";
count is incremented by one and substituted for # in the template
count: INT ← 0;
NewPressName: PUBLIC ENTRY PROCEDURE RETURNS [pressFileName: Rope.ROPE] = {
Returns a file name like "Screen1.press", "Screen2.press", etc.
ENABLE UNWIND => NULL;
i: INT = Rope.Find[s1: fileNameTemplate, s2: "#"];
pressFileName ← IF i = -1 THEN fileNameTemplate
ELSE fileNameTemplate.Replace[start: i, len: 1, with: Convert.RopeFromInt[count ← count + 1]];
};
MagnificationFactorTooLarge: PUBLIC SIGNAL = CODE;
The specified rectangle will not fit on an 8.5 by 11 inch page with the supplied margins and magnifications
AISPageAlignmentHackFailed: PUBLIC SIGNAL = CODE;
An internal error expected when the size of SirPress record definitions change (low probability)
ScreenWorker: TYPE = PROC [screenParms: ScreenParameters, vt: Terminal.Virtual, context: Imager.Context, frameBuffer: Terminal.FrameBuffer, screenWidthInWords, clippedWidth: NAT, screenPtr: LONG POINTER];
DoScreenWork: PROC [screenParms: ScreenParameters, Prepare, WithLock, Finish: ScreenWorker] = {
OPEN screenParms;
vt: Terminal.Virtual ← InterminalBackdoor.terminal;
context: Imager.Context ← ImagerTerminal.BWContext[vt: vt, pixelUnits: TRUE];
frameBuffer: Terminal.FrameBuffer ← Terminal.GetBWFrameBuffer[vt];
screenWidthInWords: NAT ← frameBuffer.wordsPerLine;
clippedWidth: NATMIN[frameBuffer.width-sourceLeft, sourceWidth];
screenPtr: LONG POINTER ← frameBuffer.base;
sourceHeight ← MIN[sourceHeight, frameBuffer.height-sourceBottom];
Prepare[screenParms: screenParms, vt: vt, context: context, frameBuffer: frameBuffer, screenWidthInWords: screenWidthInWords, clippedWidth: clippedWidth, screenPtr: screenPtr];
IF displayCarets THEN {
Carets.SuspendCarets[visible: TRUE];
};
{
Locked: SAFE PROC ~ TRUSTED {
context.MaskBox[[sourceLeft, sourceBottom, sourceLeft+borderWidth, sourceBottom+sourceHeight]];
context.MaskBox[[sourceLeft+clippedWidth-borderWidth, sourceBottom, sourceLeft+clippedWidth, sourceBottom+sourceHeight]];
context.MaskBox[[sourceLeft, sourceBottom, sourceLeft+clippedWidth, sourceBottom+borderWidth]];
context.MaskBox[[sourceLeft, sourceBottom+sourceHeight-borderWidth, sourceLeft+clippedWidth, sourceBottom+sourceHeight]];
context.SetColor[Imager.white];
context.MaskBox[[sourceLeft-16, sourceBottom, sourceLeft, sourceBottom+sourceHeight]];
context.MaskBox[[sourceLeft+clippedWidth, sourceBottom, sourceLeft+clippedWidth+16, sourceBottom+sourceHeight]];
IF displayCursor THEN {
vt: Terminal.Virtual ← Terminal.Current[];
cursorArray: Terminal.BWCursorBitmap ← Terminal.GetBWCursorPattern[vt];
position: Terminal.Position ← Terminal.GetMousePosition[vt];
cursorInfo: Cursors.CursorInfo = Cursors.GetCursorInfo[];
context.SetColor[Imager.black];
context.MaskBits[base: @cursorArray, wordsPerLine: 1, sMin: 0, fMin: 0, sSize: 16, fSize: 16, tx: position.x+cursorInfo.hotX, ty: vt.bwHeight-(position.y+cursorInfo.hotY)];
};
WithLock[screenParms: screenParms, vt: vt, context: context, frameBuffer: frameBuffer, screenWidthInWords: screenWidthInWords, clippedWidth: clippedWidth, screenPtr: screenPtr];
IF displayCursor THEN ViewerOps.PaintEverything will fix cursor up
};
IF lockViewers THEN ViewerLocks.CallUnderViewerTreeLock[Locked] ELSE Locked[];
};
IF displayCarets THEN {
Carets.ResumeCarets[];
};
ViewerOps.PaintEverything[];
Finish[screenParms: screenParms, vt: vt, context: context, frameBuffer: frameBuffer, screenWidthInWords: screenWidthInWords, clippedWidth: clippedWidth, screenPtr: screenPtr];
};
pixelsPerWord: REAL = 16.0;
micasPerPoint: REAL = 32.0; --not 2540/72.0, so we can print these press files with SPRUCE; at least, that's what Michael Plass alleged in the presence of Rick Beach and Mike Spreitzer on February 4, 1986, although experiments that day show it doesn't work.
micasPerWord: REAL = micasPerPoint * pixelsPerWord;
AISPressScreen: PUBLIC PROCEDURE
[
pressFileName: ROPE,
screenParms: ScreenParameters ← [],
pageParms: PageParameters ← []
]
RETURNS [fileName: ROPE] = TRUSTED {
OPEN screenParms, pageParms;
Converts the specified region of the bit mapped screen into a combination AIS and Press file.
Such files can be used by CedarGraphics or printed by the TSetter.
AISPressScreen can raise two signals: MagnificationFactorTooLarge when the specified rectangle will not fit on an 8.5 by 11 inch page with the supplied margins and magnifications; and AISPageAlignmentHackFailed for an internal error expected when the size of SirPress record definitions change (low probability).
trueName: Rope.ROPEIF pressFileName = NIL THEN NewAISPressName[] ELSE pressFileName;
outputStream: IO.STREAM ← FS.StreamOpen[trueName, $create];
bytesSoFar: INT;
screen: SirPress.PressHandle;
sourceLeftInWords, sourceRightInWords: NAT;
paperHeight, paperWidth, pageBorder, x, y: REAL;
bytesPerPage: INT = 2048;
wordsOfPressCommands: INT = 8; -- count of Press commands generated before dots so that the AIS image starts on a page boundary
AISHeader: TYPE = RECORD [
header: AISFileFormat.AttributeHeader,
partHeader: AISFileFormat.PartHeader,
rasterPart: AISFileFormat.RasterPart,
uca: AISFileFormat.UCACoding];
aisHeader: AISHeader ← [
header: [
password: AISFileFormat.passwordValue,
length: 0],
partHeader: [type: raster, length: SIZE[AISFileFormat.RasterPart]+SIZE[AISFileFormat.PartHeader]+SIZE[AISFileFormat.UCACoding]],
rasterPart: [
scanCount: 0,
scanLength: 0,
scanDirection: 3,
samplesPerPixel: 1,
codingType: uca],
uca: [
bitsPerSample: 0,
wordsPerScanLine: 0,
scanLinesPerBlock: 177777B,
paddingPerBlock: 177777B
]];
aisHeaderPtr: LONG POINTER TO AISHeader ← @aisHeader;
aisBlock: IO.UnsafeBlock ← [
base: LOOPHOLE[aisHeaderPtr],
startIndex: 0,
count: 2*SIZE[AISHeader]];
Prepare: ScreenWorker = CHECKED {
OPEN screenParms;
sourceLeftInWords ← MIN[sourceLeft/16, screenWidthInWords];
sourceRightInWords ← MIN[(sourceLeft+clippedWidth+15)/16, screenWidthInWords];
paperHeight ← IF landscape THEN 8.5*2540 ELSE 11*2540;
paperWidth ← (11+8.5)*2540 - paperHeight;
pageBorder ← (leftMarginInches + rightMarginInches)*2540;
SELECT scaleToFit FROM
fullPage => magnification ← MIN[
(paperWidth-pageBorder) / (micasPerWord*(sourceRightInWords-sourceLeftInWords)),
(paperHeight-pageBorder)/(micasPerPoint*sourceHeight)];
halfPage => magnification ← MIN[
(paperWidth-pageBorder) / (micasPerWord*(sourceRightInWords-sourceLeftInWords)),
(paperHeight-2*pageBorder) / (2.0*micasPerPoint*sourceHeight)];
useMagnification => NULL;
ENDCASE;
x ← (paperWidth-micasPerWord*(sourceRightInWords-sourceLeftInWords)*magnification)/2.0;
y ← (paperHeight-micasPerPoint*sourceHeight*magnification)/2.0;
IF
x<0 OR
y<0 OR
x+micasPerWord*(sourceRightInWords-sourceLeftInWords)*magnification > paperWidth OR
y+micasPerPoint*sourceHeight*magnification > paperHeight
THEN SIGNAL MagnificationFactorTooLarge;
Create the AIS Header from the screen rectangle information
aisHeader.uca.wordsPerScanLine ← sourceRightInWords-sourceLeftInWords;
aisHeader.rasterPart.scanLength ← aisHeader.uca.wordsPerScanLine*16;
aisHeader.rasterPart.scanCount ← sourceHeight;
IO.UnsafePutBlock[outputStream, aisBlock];
IO.PutChar[outputStream, '\000];
IO.PutChar[outputStream, '\000];
THROUGH [0..bytesPerPage - IO.GetIndex[outputStream] - 2*wordsOfPressCommands) DO
IO.PutChar[outputStream, '$];
ENDLOOP;
define the screen rectangle in the Press file
screen ← SirPress.Create[
fileNameForHeaderPage: trueName,
outputStream: outputStream
];
IF landscape THEN screen.SetPageSize[height: 85, width: 110, unit: SirPress.in/10];
screen.BeginScannedRectangle[
x: Real.RoundLI[x], y: Real.RoundLI[y],
numberOfLines: sourceHeight,
dotsPerLine: (sourceRightInWords-sourceLeftInWords)*16,
height: Real.RoundLI[micasPerPoint*sourceHeight*magnification],
unit: SirPress.mica];
Stuff the current position of the Press file into the AIS Header
bytesSoFar ← IO.GetIndex[outputStream];
IF bytesSoFar # bytesPerPage THEN SIGNAL AISPageAlignmentHackFailed;
aisHeader.header.length ← bytesSoFar/2;
IO.SetIndex[outputStream, 0];
IO.UnsafePutBlock[outputStream, aisBlock];
IO.SetIndex[outputStream, bytesSoFar];
};
WithLock: ScreenWorker = TRUSTED {
OPEN screenParms;
call ShowLine for each line of screen (or half-line, if left side)
FOR i: INT IN [0..sourceHeight) DO
offset: LONG CARDINAL ← (i+frameBuffer.height-sourceHeight-sourceBottom)*screenWidthInWords+sourceLeftInWords;
screen.UnsafeShowLine[screenPtr + offset];
ENDLOOP;
};
Finish: ScreenWorker = CHECKED {
OPEN screenParms;
finish the rectangle and close the file
screen.EndScannedRectangle[];
screen.ClosePress[];
};
DoScreenWork[screenParms, Prepare, WithLock, Finish];
RETURN [trueName];
};
pointsPerInch: REAL = 72.0; --not 72.27, because these are "screen points"
metersPerPoint: REAL = Imager.metersPerInch/pointsPerInch;
IPScreen: PUBLIC PROCEDURE [ipFileName: ROPE, screenParms: ScreenParameters ← [], pageParms: PageParameters ← []] RETURNS [fileName: ROPE] = {
OPEN screenParms, pageParms;
r: ImagerInterpress.Ref;
paperWidth: REAL = (IF landscape THEN 11.0 ELSE 8.5)*pointsPerInch;
paperHeight: REAL = (IF landscape THEN 8.5 ELSE 11.0)*pointsPerInch;
pageBorder: REAL = (leftMarginInches + rightMarginInches)*pointsPerInch;
x0, y0: REAL;
Prepare: ScreenWorker = {
SELECT scaleToFit FROM
fullPage => magnification ← MIN[
(paperWidth-pageBorder) / sourceWidth,
(paperHeight-pageBorder) / sourceHeight];
halfPage => magnification ← MIN[
(paperWidth-pageBorder) / sourceWidth,
(paperHeight-2*pageBorder) / 2.0*sourceHeight];
useMagnification => NULL;
ENDCASE;
x0 ← (paperWidth-sourceWidth*magnification)/2.0;
y0 ← (paperHeight-sourceHeight*magnification)/2.0;
IF
x0<0 OR
y0<0 OR
x0+sourceWidth*magnification > paperWidth OR
y0+sourceHeight*magnification > paperHeight
THEN SIGNAL MagnificationFactorTooLarge;
};
WithLock: ScreenWorker = {
OPEN screenParms;
DoIPMaster: PROC [context: Imager.Context] = {
context.ScaleT[metersPerPoint];
IF landscape THEN {context.TranslateT[t: [paperHeight, 0]]; context.RotateT[90]};
context.TranslateT[t: [x0, y0]];
context.ScaleT[magnification];
context.TranslateT[t: [-sourceLeft, -sourceBottom]];
context.ClipRectangle[[sourceLeft, sourceBottom, sourceWidth, sourceHeight]];
context.MaskBits[
base: screenPtr,
wordsPerLine: frameBuffer.wordsPerLine,
sMin: frameBuffer.height-sourceHeight-sourceBottom,
fMin: sourceLeft,
sSize: sourceHeight,
fSize: sourceWidth,
tx: sourceLeft,
ty: sourceBottom+sourceHeight
];
};
ImagerInterpress.DoPage[self: r, action: DoIPMaster !
Imager.Error => {
MessageWindow.Append[
message: Rope.Concat[error.explanation, " creating IPMaster"],
clearFirst: TRUE];
CONTINUE}
];
};
Finish: ScreenWorker = {
};
fileName ← IF ipFileName = NIL THEN NewIPName[] ELSE ipFileName;
r ← ImagerInterpress.Create[fileName: fileName ! FS.Error => {
MessageWindow.Append[message: error.explanation, clearFirst: TRUE];
GOTO Quit;
}];
DoScreenWork[screenParms, Prepare, WithLock, Finish];
ImagerInterpress.Close[r];
EXITS
Quit => NULL;
};
StuffScreen: PUBLIC PROCEDURE [screenParms: ScreenParameters ← []] = {
OPEN screenParms;
Prepare: ScreenWorker = {
};
WithLock: ScreenWorker = {
OPEN screenParms;
DoStuff: PROC [context: Imager.Context] = {
context.TranslateT[t: [-sourceLeft, -sourceBottom]];
context.MaskBits[
base: screenPtr,
wordsPerLine: frameBuffer.wordsPerLine,
sMin: frameBuffer.height-sourceHeight-sourceBottom,
fMin: sourceLeft,
sSize: sourceHeight,
fSize: sourceWidth,
tx: sourceLeft,
ty: sourceBottom+sourceHeight
];
};
ImagerArtwork.PasteArtwork[action: DoStuff, bounds: [0, 0, sourceWidth, sourceHeight], m: ImagerArtwork.Points[], clip: TRUE];
};
Finish: ScreenWorker = {
};
DoScreenWork[screenParms, Prepare, WithLock, Finish];
};
aisFileNameTemplate: ROPE = "Screen#.AIS";
NewAISPressName: PUBLIC ENTRY PROCEDURE RETURNS [pressFileName: ROPE] = {
Returns a file name like "Screen1.ais", "Screen2.ais", etc.
ENABLE UNWIND => NULL;
NextName: PROCEDURE RETURNS [ROPE] = {
RETURN [aisFileNameTemplate.Replace[start: i, len: 1, with: Convert.RopeFromInt[count ← count + 1]]];
};
i: INT = Rope.Find[s1: aisFileNameTemplate, s2: "#"];
pressFileName ← IF i = -1 THEN aisFileNameTemplate
ELSE NextName[];
DO
s: FS.OpenFile;
s ← FS.Open[pressFileName ! FS.Error => GO TO ThisNameIsOkay];
FS.Close[s];
pressFileName ← NextName[];
ENDLOOP;
EXITS
ThisNameIsOkay => NULL;
};
ipFileNameTemplate: ROPE = "Screen#.IP";
NewIPName: PUBLIC ENTRY PROCEDURE RETURNS [ipFileName: ROPE] = {
Returns a file name like "Screen1.IP", "Screen2.IP", etc.
ENABLE UNWIND => NULL;
NextName: PROCEDURE RETURNS [ROPE] = {
RETURN [ipFileNameTemplate.Replace[start: i, len: 1, with: Convert.RopeFromInt[count ← count + 1]]];
};
i: INT = Rope.Find[s1: ipFileNameTemplate, s2: "#"];
ipFileName ← IF i = -1 THEN ipFileNameTemplate
ELSE NextName[];
DO
s: FS.OpenFile;
s ← FS.Open[ipFileName ! FS.Error => GO TO ThisNameIsOkay];
FS.Close[s];
ipFileName ← NextName[];
ENDLOOP;
EXITS
ThisNameIsOkay => NULL;
};
}.
6-Apr-82 12:01:31 Teitelman fixed PressScreenButton to clear message window first.
May 4, 1982 4:39 pm Cattell: print msg saying whether half or full screen. Also, no longer need config 'cause SirPressPackage is external.
June 18, 1982 1:58 pm Cattell: convert to 3.2; leave a little left margin to avoid printer truncation.
November 15, 1982 12:30 pm Plass: Made client-callable interface.
November 15, 1982 2:30 pm Plass: Made rightSide work, removed button interface, made half-screen resolution match what Spruce can sometimes handle.
November 16, 1982 10:57 am Plass: Tracked SirPress changes.
February 21, 1984 10:12:39 am PST Plass: Fixed AIS header to make a proper AIS file.
April 25, 1985 11:05:01 am PST: Imager conversion.