<> <> <> <> <> <> <> <> <> <> <> <> DIRECTORY Atom, CD, CDApplications, CDBasics, CDCallSpecific, CDViewer, CDEvents, CDExtras, CDIO, CDMenus, CDOps, CDProperties, CDSequencer, CornerStitching, FS, Graphics, GraphicsOps, Menus, IO, Process, Real, Rope, STPStreams, TerminalIO, VFonts, ViewerClasses, ViewerOps, ViewerSpecs; CDBWVersatecImpl: CEDAR MONITOR IMPORTS Atom, CD, CDApplications, CDBasics, CDCallSpecific, CDEvents, CDExtras, CDIO, CDMenus, CDViewer, CDOps, CDProperties, CDSequencer, CornerStitching, FS, Graphics, GraphicsOps, IO, Menus, Process, Real, STPStreams, TerminalIO, VFonts, ViewerOps EXPORTS = BEGIN maxScanLineWidth: CARDINAL = 7040; -- the wide-bed Versatec scanLineWidth: CARDINAL _ maxScanLineWidth; maxPatchHeight: CARDINAL = 144; -- fits in 64k stippleRepeat: CARDINAL = 16; Nibble: TYPE = [0..16); ScanLine: TYPE = PACKED ARRAY [0..0) OF WORD; ScanLineInNibbles: TYPE = PACKED ARRAY [0..0) OF Nibble; CodedScanLine: TYPE = RECORD [ codeWordCount: CARDINAL _ 0, codeWords: REF CodeWords ]; CodeWords: TYPE = PACKED ARRAY [0..maxScanLineWidth) OF CodeWord; CodeWord: TYPE = MACHINE DEPENDENT RECORD [ repeats (0: 0..3): [1..16], pattern (0: 4..7): Nibble]; textures: ARRAY CD.Layer OF REF GraphicsOps.Texture _ ALL[NIL]; outLine: BOOL _ TRUE; skipLayers: BOOL _ FALSE; allBlack: BOOL _ FALSE; handleSignalNames: BOOL _ TRUE; PlotStateRef: TYPE = REF PlotState; PlotState: TYPE = RECORD [ text: REF CornerStitching.Tesselation _ NIL, tes: ARRAY CD.Layer OF REF CornerStitching.Tesselation _ ALL[NIL], bitmap: GraphicsOps.BitmapRef _ NIL, vPatch, context: Graphics.Context _ NIL, scale: REAL _ 1.0, -- pixels per CD.DesignNumber totalPlotClip, bandClip: CD.DesignRect _ [0, 0, 0, 0], <> texture: GraphicsOps.Texture _ ALL[0] ]; currentFlavor: ViewerClasses.ViewerFlavor = $ChipndaleVersatecPlot; Init: ENTRY PROC ~ { menu: Menus.Menu = Menus.CreateMenu[]; Menus.AppendMenuEntry[menu: menu, entry: Menus.CreateEntry[name: "AbortPlot", proc: AbortProc, guarded: TRUE]]; CDEvents.RegisterEventProc[$Abort, AbortEvent]; ViewerOps.RegisterViewerClass[flavor: currentFlavor, class: NEW[ViewerClasses.ViewerClassRec _ [paint: PaintVersatecStripe, menu: menu]]]; CDSequencer.ImplementCommand[a: $VPlotLocal, p: PlotDesignToLocalFile, queue: doQueue]; CDSequencer.ImplementCommand[a: $VPlotPlotter, p: PlotDesignToPlotter, queue: doQueue]; CDSequencer.ImplementCommand[a: $VPlot, p: PlotDesignToAny, queue: doQueue]; CDSequencer.ImplementCommand[a: $VersatecBWPlot, p: PlotDesignToAny, queue: doQueue]; CDMenus.CreateEntry[menu: $HardCopyMenu, entry: "BW Versatec Plot", key: $VersatecBWPlot]; signalFont _ VFonts.GraphicsFont[VFonts.EstablishFont[family: "Helvetica", size: 18, bold: TRUE]]; TerminalIO.WriteRope["ChipNDale BW Versatec plotter loaded\n"]; TerminalIO.WriteRope[" ..use the hardcopy command for plotting..\n"]; }; <<>> PlotDesignToAny: ENTRY PROC [ c: CDSequencer.Command ] = BEGIN ENABLE UNWIND => NULL; n: CARDINAL _ TerminalIO.RequestSelection[ label: "Versatec Plot", choice: LIST[" plotter", " local", " read name"] ]; SELECT n FROM 1 => PlotDesign[c: c, to: plotter]; 2 => PlotDesign[c: c, to: localFile]; 3 => PlotDesign[c: c, to: requestFilename]; ENDCASE => TerminalIO.WriteRope["skipped\n"]; END; PlotDesignToLocalFile: ENTRY PROC [ c: CDSequencer.Command ] = BEGIN ENABLE UNWIND => NULL; PlotDesign[c: c, to: localFile]; END; PlotDesignToPlotter: ENTRY PROC [ c: CDSequencer.Command ] = BEGIN ENABLE UNWIND => NULL; PlotDesign[c: c, to: plotter]; END; signalFont: Graphics.FontRef _ NIL; viewer: ViewerClasses.Viewer _ NIL; plotter: Rope.ROPE _ "vice"; plotFileToSpool: Rope.ROPE _ "[vice]plot.bits"; plotFileOnPlotter: Rope.ROPE _ "plot.bits"; plotFileOnLocalMachine: Rope.ROPE _ "///chipndale/tem/plot.bits"; latestPlotFile: Rope.ROPE _ NIL; spoolToPlotter: BOOL _ TRUE; abortPlot: BOOL _ FALSE; designIfAbort: CD.Design _ NIL; AssignTextures: PROC [usedLayers: REF PACKED ARRAY CD.Layer OF BOOL] = BEGIN IF allBlack THEN { black: REF GraphicsOps.Texture _ NEW[GraphicsOps.Texture_ALL[0FFH]]; FOR l: CD.Layer IN CD.Layer DO -- assign textures to used layers textures[l] _ black; ENDLOOP; } ELSE { textureNo: CARDINAL _ 0; FOR l: CD.Layer IN CD.Layer DO -- assign textures to used layers IF usedLayers[l] THEN { textures[l] _ Texture[textureNo]; textureNo _ textureNo+1 } ELSE textures[l] _ NIL; ENDLOOP; } END; PlotDesign: INTERNAL PROC [ c: CDSequencer.Command, to: {localFile, plotter, requestFilename}] = BEGIN s: IO.STREAM _ NIL; plotClip: CD.DesignRect; strips: [1..100]; IF ~outLine OR skipLayers OR allBlack OR ~handleSignalNames THEN TerminalIO.WriteRope["**WARNING, colors set up different from default\n"]; SELECT TerminalIO.RequestSelection["Plot", LIST["complete design", "rectangle"]] FROM 2 => { plotClip _ CDBasics.ToRect[c.pos, c.sPos]; TerminalIO.WriteRope["plot rectangle\n"]; }; ENDCASE => { plotClip _ CDExtras.BoundingBox[c.design]; TerminalIO.WriteRope["plot all\n"]; }; strips _ TerminalIO.RequestInt["How many vertical strips in the plot? "]; BEGIN ENABLE { -- for ERRORs UNWIND => {s _ AbortFile[s]; TerminalIO.WriteRope[" ** plot aborted ** "]}; }; design: CD.Design = c.design; dr: CD.DrawRef = CD.CreateDrawRef[design]; scale: REAL = MIN[32.0/CD.lambda, (REAL[scanLineWidth]+REAL[scanLineWidth-100]*(strips-1))/(plotClip.x2-plotClip.x1)]; <<---Center the x range of the selected area of the design on the plotter bed, with at most 32 pixels per lambda. If multiple strips are called for, overlap adjacent ones by 100 pixels.>> patchHeight: CARDINAL = stippleRepeat*((MIN[maxPatchHeight, CARDINAL[Real.Fix[50*CD.lambda*scale]]]+stippleRepeat-1)/stippleRepeat); <<--divisible by stippleRepeat, so that textures work out>> bitmap: GraphicsOps.BitmapRef = GraphicsOps.NewBitmap[width: scanLineWidth, height: patchHeight]; ps: PlotStateRef = NEW[ PlotState _ [ text: CornerStitching.NewTesselation[], scale: scale, totalPlotClip: plotClip, bitmap: bitmap, vPatch: GraphicsOps.NewContextFromBitmap[bitmap]] ]; usedLayers: REF PACKED ARRAY CD.Layer OF BOOL _ NIL; abortPlot _ FALSE; designIfAbort _ design; TerminalIO.WriteRope["Starting Versatec plot "]; TRUSTED {Process.SetPriority[Process.priorityBackground]}; viewer _ ViewerOps.FindViewer["Versatec Stripe"]; IF viewer # NIL AND viewer.class.flavor # currentFlavor THEN {ViewerOps.DestroyViewer[viewer]; viewer _ NIL}; IF viewer=NIL THEN BEGIN viewer _ ViewerOps.CreateViewer[flavor: currentFlavor, info: [name: "Versatec Stripe", iconic: FALSE, openHeight: patchHeight+ViewerSpecs.menuHeight]]; END; viewer.data _ ps; dr.minimalSize _ 0; dr.drawRect _ dr.saveRect _ NoteLayer; dr.contextFilter _ NEW[CD.ContextFilter_ALL[[doit: TRUE]]]; dr.drawContext _ CheckDrawContext; dr.devicePrivate _ usedLayers; dr.interestClip _ CDBasics.universe; dr.devicePrivate _ usedLayers _ NEW[PACKED ARRAY CD.Layer OF BOOL _ ALL[FALSE]]; CDOps.DrawDesign[design, dr]; -- mark used layers AssignTextures[usedLayers]; dr.drawRect _ dr.saveRect _ NoteRectangle; dr.contextFilter _ NIL; dr.devicePrivate _ ps; SELECT to FROM localFile => BEGIN TerminalIO.WriteRope["onto """]; s _ FS.StreamOpen[plotFileOnLocalMachine, create]; TerminalIO.WriteRope[plotFileOnLocalMachine]; TerminalIO.WriteRope[""""]; s.SetLength[0]; latestPlotFile _ plotFileOnLocalMachine; END; requestFilename => BEGIN r: Rope.ROPE _ CDIO.MakeName[TerminalIO.RequestRope["to file ? > "], "bits"]; TerminalIO.WriteRope["onto """]; s _ FS.StreamOpen[r, create]; TerminalIO.WriteRope[r]; TerminalIO.WriteRope[""""]; s.SetLength[0]; latestPlotFile _ r; END; plotter => BEGIN TerminalIO.WriteRope["onto plotter "]; [self: s] _ STPStreams.OpenSTP[host: plotter, fileName: plotFileOnPlotter, accessOptions: create]; latestPlotFile _ NIL; END; ENDCASE => ERROR; TRUSTED { s.PutChar[LOOPHOLE[scanLineWidth/256, CHARACTER]]; s.PutChar[LOOPHOLE[scanLineWidth MOD 256]]; <> }; <<>> IF handleSignalNames THEN { TerminalIO.WriteRope["\n.. found "]; TerminalIO.WriteInt[CDCallSpecific.CallForAll[design: design, whatElse: LookForSignalName, x: ps]]; TerminalIO.WriteRope[" top-layer signal names "]; }; SendWhiteBar[s: s]; SendBlackBar[s: s]; FOR strip: INT IN [0..strips) DO clip: CD.DesignRect = [ x1: plotClip.x1+strip*((plotClip.x2-plotClip.x1)/strips), y1: plotClip.y1, x2: plotClip.x1+(strip+1)*((plotClip.x2-plotClip.x1)/strips)+Real.Fix[100./scale]+1, y2: plotClip.y2]; FOR topLine: INT _ 0, topLine+patchHeight WHILE topLine> [] _ ps.vPatch.SetPaintMode[transparent]; ps.vPatch.SetColor[Graphics.black]; ps.context _ ps.vPatch.CopyContext[]; ps.context.Scale[sx: scale, sy: scale]; [dx: x, dy: y] _ Graphics.Map[sc: ps.vPatch, dc: ps.context, sx: scanLineWidth/2, sy: topLine+patchHeight-1]; ps.context.Translate[tx: x-(clip.x2+clip.x1)/2, ty: y-clip.y2]; ps.context.ClipBox[box: [xmin: clip.x1, ymin: clip.y1, xmax: clip.x2, ymax: clip.y2]]; <> [dx: x1, dy: y1] _ Graphics.Map[sc: ps.vPatch, dc: ps.context, sx: -1, sy: -1]; [dx: x2, dy: y2] _ Graphics.Map[sc: ps.vPatch, dc: ps.context, sx: scanLineWidth, sy: patchHeight]; dc _ CDBasics.NormalizeRect[[x1: Real.Fix[x1], y1: Real.Fix[y1], x2: Real.Fix[x2], y2: Real.Fix[y2]]]; ps.bandClip _ dr.interestClip _ [x1: MAX[plotClip.x1, dc.x1-1], y1: MAX[plotClip.y1, dc.y1-1], x2: MIN[plotClip.x2, dc.x2+1], y2: MIN[plotClip.y2, dc.y2+1]]; <> CDViewer.ShowArrow[design: design, pos: [x: (dr.interestClip.x1+dr.interestClip.x2)/2, y: (dr.interestClip.y1+dr.interestClip.y2)/2]]; FOR l: CD.Layer IN CD.Layer DO IF ps.tes[l]#NIL THEN CornerStitching.ChangeRect[plane: ps.tes[l], rect: CDBasics.universe, newValue: NIL]; ENDLOOP; CDOps.DrawDesign[design, dr]; -- build tesselations of the relevant design rectangle AnalyzeTesselations[ps]; [] _ ps.vPatch.SetPaintMode[opaque]; [] _ ps.text.EnumerateArea[rect: ps.totalPlotClip, perTile: PaintASignalName, data: ps]; ViewerOps.PaintViewer[viewer: viewer, hint: all, clearClient: topLine=0]; <> SendBitmap[s: s, bitmap: ps.bitmap]; s.Flush[]; TerminalIO.WriteRope["."]; ENDLOOP; TerminalIO.WriteRope["*"]; SendBlackBar[s: s]; ENDLOOP; SendWhiteBar[s: s]; s.Close[]; IF to # plotter AND spoolToPlotter THEN SpoolFileToPlotter[latestPlotFile]; designIfAbort _ NIL; TerminalIO.WriteRope["finished\n"]; EXITS AbortPlot => {s _ AbortFile[s]; TerminalIO.WriteRope[" ** plot aborted ** "]; abortPlot _ FALSE}; END; END; CheckDrawContext: PROC [pr: CD.DrawRef, proc: CD.DrawContextLayerProc, ob: CD.ObPtr, pos: CD.DesignPosition, orient: CD.Orientation, layer: CD.Layer] = BEGIN IF pr.contextFilter#NIL AND pr.contextFilter[layer].doit THEN { pr.contextFilter[layer].doit _ FALSE; TerminalIO.WriteRope[" "]; TerminalIO.WriteRope[CDOps.Info[ob]]; TerminalIO.WriteRope[" (and maybe others) is not rectilinear and will not appear on plot.\n"]; } END; NoteLayer: PROC [ r: CD.DesignRect, l: CD.Layer, pr: CD.DrawRef ] = BEGIN ps: REF PACKED ARRAY CD.Layer OF BOOL = NARROW[pr.devicePrivate]; ps[l] _ TRUE; END; NoteRectangle: PROC [ r: CD.DesignRect, l: CD.Layer, pr: CD.DrawRef ] = BEGIN ps: PlotStateRef = NARROW[pr.devicePrivate]; IF CDBasics.NonEmpty[r] THEN { IF ps.tes[l]=NIL THEN { IF skipLayers AND CDProperties.GetPropFromLayer[l, $DrawMeOnBWVersatec]#$DrawMeOnBWVersatec THEN RETURN; ps.tes[l] _ CornerStitching.NewTesselation[]; }; ps.tes[l].ChangeRect[rect: r, newValue: $covered]; }; END; AnalyzeTesselations: PROC [ ps: PlotStateRef ] = BEGIN FOR l: CD.Layer IN CD.Layer DO IF ps.tes[l]#NIL THEN { ps.texture _ textures[l]^; [] _ ps.tes[l].EnumerateArea[rect: ps.bandClip, perTile: ProcessTile, data: ps, backgroundValue: $none]; }; ENDLOOP; END; ProcessTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY] = -- CornerStitching.PerTileProc -- BEGIN -- only called on covered tiles t: CornerStitching.TilePtr; ps: PlotStateRef = NARROW[data]; tileValue: REF = tile.Value; IF tileValue = $covered THEN { r: CD.DesignRect = tile.Area; GraphicsOps.DrawTexturedBox[self: ps.context, box: [xmin: r.x1, ymin: r.y1, xmax: r.x2, ymax: r.y2], texture: ps.texture]; }; IF outLine THEN { <<--Put lines at boundaries with regions of other color>> FOR t _ tile.NEastNeighbour, t.WSouthNeighbour WHILE tile.SouthEdge> IF t.Value#tileValue THEN { ps.context.SetCP[x: tile.EastEdge, y: MAX[tile.SouthEdge, t.SouthEdge]]; ps.context.DrawTo[x: tile.EastEdge, y: MIN[tile.NorthEdge, t.NorthEdge]]; }; ENDLOOP; FOR t _ tile.ENorthNeighbour, t.SWestNeighbour WHILE tile.WestEdge> IF t.Value#tileValue THEN { ps.context.SetCP[x: MAX[tile.WestEdge, t.WestEdge], y: tile.NorthEdge]; ps.context.DrawTo[x: MIN[tile.EastEdge, t.EastEdge], y: tile.NorthEdge]; }; ENDLOOP; }; END; SignalValueRef: TYPE = REF SignalValue; SignalValue: TYPE = RECORD [s: Rope.ROPE]; LookForSignalName: PROC [ design: CD.Design, aptr: CD.ApplicationPtr, x: REF ] RETURNS [done: BOOL_TRUE, removeMe: BOOL_FALSE, include: CD.ApplicationList_NIL, repaintMe: BOOL_FALSE, repaintInclude: BOOL_FALSE] -- CDCallSpecific.CallProc -- = BEGIN <> GetSignalName: PROC [signal: REF] RETURNS [s: Rope.ROPE] = BEGIN WITH signal SELECT FROM atom: ATOM => s _ Atom.GetPName[atom]; rope: Rope.ROPE => s _ rope; ENDCASE => s _ "?"; END; ps: PlotStateRef = NARROW[x]; r: CD.DesignRect = CDBasics.Intersection[ps.totalPlotClip, CDApplications.ARectO[aptr]]; signal: REF = CDProperties.GetProp[from: aptr, prop: $SignalName]; IF signal # NIL AND CDBasics.NonEmpty[r] THEN BEGIN signalValue: SignalValueRef = NEW[SignalValue _ [s: GetSignalName[signal]]]; others: LIST OF REF CornerStitching.Region = NARROW[ps.text.EnumerateArea[rect: r]]; ps.text.ChangeRect[rect: r, newValue: signalValue]; FOR other: LIST OF REF CornerStitching.Region _ others, other.rest WHILE other#NIL DO ps.text.ChangeRect[rect: other.first.rect, newValue: other.first.value]; ENDLOOP; END ELSE done _ FALSE; END; PaintASignalName: PROC [tile: CornerStitching.TilePtr, data: REF ANY] = <> BEGIN ps: PlotStateRef = NARROW[data]; textContext: Graphics.Context = ps.vPatch.CopyContext[]; signalValue: SignalValueRef = NARROW[tile.Value[]]; dx, dy, xmin, xmax, ymin, ymax: REAL; r: CD.DesignRect = CDBasics.Intersection[tile.Area, ps.totalPlotClip]; ctr: CD.DesignPosition = Center[r]; [dx: dx, dy: dy] _ Graphics.Map[sc: ps.context, dc: ps.vPatch, sx: ctr.x, sy: ctr.y]; [xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax] _ Graphics.RopeBox[font: signalFont, rope: signalValue.s]; textContext.Translate[tx: dx-(xmax+xmin)/2, ty: dy-(ymax+ymin)/2]; -- centers the text in r IF ((xmax-xmin)>(ymax-ymin)) # ((r.x2-r.x1)>(r.y2-r.y1)) THEN textContext.Rotate[270]; textContext.SetColor[Graphics.white]; textContext.DrawBox[[xmin: -2, ymin: -2, xmax: xmax-xmin+2, ymax: ymax-ymin+2]]; <> textContext.SetColor[Graphics.black]; textContext.SetCP[x: 0, y: 0]; textContext.DrawRope[rope: signalValue.s, font: signalFont]; END; Center: PROC [ r: CD.Rect ] RETURNS [ CD.Position ] = {RETURN[[x: (r.x1+r.x2)/2, y: (r.y1+r.y2)/2]]}; SendWhiteBar: PROC [ s: IO.STREAM, lines: CARDINAL _ 40 ] = {SendBar[s, 0, lines]}; SendBlackBar: PROC [ s: IO.STREAM, lines: CARDINAL _ 20 ] = {SendBar[s, 15, lines]}; SendBar: PROC [ s: IO.STREAM, pattern: Nibble, lines: CARDINAL ] = BEGIN scanLineNibbles: CARDINAL = scanLineWidth/4; FOR j: CARDINAL IN [0..lines) DO FOR i: CARDINAL IN [0..(scanLineNibbles+15)/16) DO TRUSTED {s.PutChar[LOOPHOLE[CodeWord[repeats: MIN[16, scanLineNibbles-16*i], pattern: pattern]]]}; ENDLOOP; ENDLOOP; END; SendBitmap: PROC [ s: IO.STREAM, bitmap: GraphicsOps.BitmapRef ] = BEGIN csl: REF CodedScanLine = NEW[CodedScanLine _ [codeWords: NEW[CodeWords]]]; FOR i: CARDINAL IN [0..bitmap.height) DO -- send to the Versatec TRUSTED { EncodeScanLine[sl: LOOPHOLE[LOOPHOLE[bitmap.base, INT]+INT[bitmap.raster]*i, LONG POINTER TO ScanLine], bitWidth: bitmap.width, csl: csl]; s.UnsafePutBlock[[base: LOOPHOLE[csl.codeWords], startIndex: 0, count: csl.codeWordCount]]; } ENDLOOP; END; EncodeScanLine: PROC [ sl: LONG POINTER TO ScanLine, bitWidth: CARDINAL, csl: REF CodedScanLine ] = TRUSTED BEGIN size: CARDINAL _ 0; scanLineInNibbles: LONG POINTER TO ScanLineInNibbles = LOOPHOLE[sl]; scanLineNibbles: CARDINAL = (bitWidth+3)/4; i: CARDINAL _ 0; WHILE i Stipple[01H, 02H, 04H, 08H], 1 => Stipple[04H, 02H, 01H, 08H], 2 => Stipple[00H, 0AH, 00H, 0AH], 3 => Stipple[08H, 00H, 08H, 00H], 4 => Stipple[0AH, 00H, 00H, 00H], 5 => Stipple[08H, 00H, 02H, 00H], 6 => Stipple[00H, 01H, 00H, 04H], 7 => Stipple[04H, 04H, 00H, 00H], 8 => Stipple[00H, 00H, 00H, 03H], ENDCASE => Stipple[0FH, 0FH, 0FH, 0FH])[row]; ENDLOOP; RETURN[t]; END; PaintVersatecStripe: PROC [ self: ViewerClasses.Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL ] -- ViewerClasses.PaintProc -- = BEGIN ps: PlotStateRef = NARROW[self.data]; x: INT = MAX[INT[0], (ps.bitmap.width-self.cw)/2]; y: INT = MAX[INT[0], (ps.bitmap.height-self.ch)/2]; context.SetCP[self.cw/2, self.ch/2]; GraphicsOps.DrawBitmap[self: context, bitmap: ps.bitmap, w: MIN[self.cw, ps.bitmap.width], h: MIN[self.ch, ps.bitmap.height], x: x, y: y, xorigin: ps.bitmap.width/2, yorigin: ps.bitmap.height/2]; END; AbortProc: Menus.MenuProc = {abortPlot _ TRUE}; AbortEvent: CDEvents.EventProc = { IF designIfAbort#NIL AND (design=NIL OR design=designIfAbort) THEN { abortPlot _ TRUE; designIfAbort _ NIL } }; AbortFile: PROC [s: IO.STREAM ] RETURNS [ IO.STREAM ] = {IF s#NIL THEN s.Close[abort: TRUE]; RETURN[NIL]}; SpoolToPlotter: ENTRY PROC = {SpoolFileToPlotter[latestPlotFile]}; SpoolFileToPlotter: PROC [name: Rope.ROPE] = BEGIN IF name # NIL THEN { TerminalIO.WriteRope["spool file """]; TerminalIO.WriteRope[name]; TerminalIO.WriteRope[""".."]; FS.Copy[from: name, to: plotFileToSpool ! FS.Error => IF error.group # bug THEN { TerminalIO.WriteRope[" not done: "]; TerminalIO.WriteRope[error.explanation]; TerminalIO.WriteLn[]; CONTINUE; } ]; TerminalIO.WriteRope[" spool finished\n"]; }; END; <<>> <> <<>> Init[]; END. -- of VersatecImpl