<<>> <> <> <> <> <> <> <> <> <> DIRECTORY Atom, CedarProcess, Commander, Convert, FileNames, FS, Imager, ImagerBackdoor, ImagerColor, ImagerFont, ImagerInterpress, ImagerPrivate, ImagerSample, IO, NodeProps, PFS, Real, Rope, TextEdit, TextNode, TiogaImager, TiogaIO, UserProfile, Vector2; TiogaToInterpressImpl: CEDAR PROGRAM IMPORTS CedarProcess, Commander, Convert, FileNames, FS, Imager, ImagerBackdoor, ImagerColor, ImagerInterpress, ImagerSample, IO, NodeProps, PFS, Real, Rope, TextEdit, TextNode, TiogaImager, TiogaIO, UserProfile EXPORTS Imager ~ BEGIN ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Vector2.VEC; ActionProc: TYPE ~ PROC [data: ParsedCommand, msgStream: IO.STREAM]; LocalError: ERROR [rope: ROPE] ~ CODE; AppendFix: PROC [node: TextNode.Ref, name: ATOM, value: ROPE] ~ { old: ROPE ~ NodeProps.GetSpecs[name: name, value: NodeProps.GetProp[n: node, name: name]]; new: ROPE ~ Rope.Cat[old, " ", value]; NodeProps.PutProp[n: node, name: name, value: NodeProps.DoSpecs[name: name, specs: new]]; }; GetWithStyle: PROC [inputName: ROPE, styleName: ROPE ¬ NIL, rootPrefix, rootPostfix: ROPE ¬ NIL] RETURNS [root: TextNode.Ref] ~ { fullFName: ROPE ¬ NIL; root ¬ TiogaIO.FromFile[PFS.PathFromRope[inputName]].root; IF Rope.Size[styleName]#0 THEN TextEdit.ChangeStyle[node: root, name: styleName, event: NIL]; IF rootPrefix # NIL THEN AppendFix[root, $Prefix, rootPrefix]; IF rootPostfix # NIL THEN AppendFix[root, $Postfix, rootPostfix]; }; ptpermeter: REAL ¬ 72.27/0.0254; ptperbp: REAL ¬ 72.27/72; MakeHeader: PROC [version: REAL] RETURNS [ROPE] ~ { header: ROPE ~ IO.PutFR1["Interpress/Xerox/%g ", [real[version]]]; RETURN [header] }; PredeclareSetFont: PROC [context: Imager.Context, font: ImagerFont.Font] = { refMaster: REF ImagerInterpress.Ref ~ NARROW[Imager.GetProp[context: context, key: $Master]]; oldClass: Class ~ NARROW[Imager.GetProp[context: context, key: $OldClass]]; oldClass.SetFont[context, font]; ImagerInterpress.DeclareFont[self: refMaster­, font: font]; }; Class: TYPE ~ REF ClassRep; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; PredeclareFonts: PROC [master: ImagerInterpress.Ref, paint: PROC [context: Imager.Context]] ~ { context: Imager.Context ~ ImagerBackdoor.BitmapContext[bitmap: ImagerSample.NewSampleMap[box: [[0,0], [0,0]]]]; oldClass: Class ~ context.class; newClass: Class ~ NEW[ClassRep ¬ oldClass­]; newClass.SetFont ¬ PredeclareSetFont; context.class ¬ newClass; Imager.SetNoImage[context, TRUE]; Imager.PutProp[context: context, key: $Master, val: NEW[ImagerInterpress.Ref ¬ master]]; Imager.PutProp[context: context, key: $OldClass, val: oldClass]; paint[context]; }; TiogaToInterpressAction: PROC [data: ParsedCommand, msgStream: IO.STREAM] ~ { master: ImagerInterpress.Ref ~ ImagerInterpress.Create[data.outputName, MakeHeader[data.version]]; pageIndexFileName: ROPE ~ ExtendName[data.outputName, "pageIndex"]; pageIndexStream: IO.STREAM ~ IF data.pageIndexing THEN FS.StreamOpen[pageIndexFileName, $create] ELSE NIL; pageCount: INT ¬ 0; prevCharIndex: INT ¬ 0; FOR inputs: LIST OF ROPE ¬ data.inputNames, inputs.rest WHILE inputs#NIL AND pageCount-data.skipPages < data.nPages DO marks: Atom.PropList ¬ NIL; root: TextNode.Ref ~ GetWithStyle[inputs.first, data.style, data.rootPrefix, data.rootPostfix]; loc: TextNode.Location ¬ [node: TextNode.StepForward[root], where: 0]; IF data.pageIndexing THEN IO.PutF1[pageIndexStream, "(\"%g\"\n ", [rope[inputs.first]]]; IF inputs # data.inputNames THEN { msgStream.PutRope[" . . . "]; msgStream.PutRope[inputs.first]; msgStream.PutRope[" . . . "]; }; WHILE loc.node # NIL AND pageCount-data.skipPages < data.nPages DO levelClipFilter: TiogaImager.FilterProc ~ { IF level>data.levelClip THEN skip ¬ TRUE }; filter: TiogaImager.FilterProc ~ IF data.levelClip#0 THEN levelClipFilter ELSE NIL; page: TiogaImager.FormattedPage; paint: PROC [context: Imager.Context] ~ { <> Imager.ScaleT[context, 0.0254]; -- now we've got inches Imager.TranslateT[context, data.translate]; Imager.ScaleT[context, 1.0/72.27]; -- now we've got Tioga screen dots Imager.ScaleT[context, data.magnify]; Imager.RotateT[context, data.rotate]; IF data.background#NIL THEN { Imager.SetColor[context, data.background]; Imager.MaskRectangle[context, [-10000, -10000, 20000, 20000]]; Imager.SetColor[context, Imager.black]; }; TiogaImager.Render[page.box, context, [0, 0]]; }; IF data.verbose AND pageCount >= data.skipPages THEN { msgStream.PutRope["\n (Location "]; msgStream.PutRope[Convert.RopeFromInt[TextNode.LocNumber[at: loc, skipCommentNodes: TRUE]]]; msgStream.PutRope[") "]; }; page ¬ TiogaImager.FormatPage[pageCounter: pageCount, startLoc: loc, filter: filter, marks: marks, styleKind: IF data.screenFormat THEN screen ELSE print]; IF pageCount = 0 THEN { PredeclareFonts[master, paint]; }; IF pageCount >= data.skipPages THEN { charIndex: INT ~ IF page.nextLoc.node # NIL THEN TextNode.LocNumber[at: page.nextLoc, skipCommentNodes: FALSE] - 1 ELSE TextNode.LocNumber[at: TextNode.LastLocWithin[n: root], skipCommentNodes: FALSE]; figure: ROPE ~ Convert.RopeFromInt[page.pageFigure]; msgStream.PutRope["["]; msgStream.PutRope[figure]; <> ImagerInterpress.DoPage[master, paint, 1.0]; msgStream.PutRope["] "]; IF pageCount = data.skipPages THEN prevCharIndex ¬ TextNode.LocNumber[at: loc, skipCommentNodes: FALSE]; IF data.pageIndexing THEN IO.PutF[pageIndexStream, "(\"%g\" (%g %g))\n ", [rope[figure]], [integer[prevCharIndex]], [integer[charIndex]]]; prevCharIndex ¬ charIndex+1; }; TiogaImager.Destroy[page.box]; pageCount ¬ pageCount + 1; marks ¬ page.marks; loc ¬ page.nextLoc; ENDLOOP; IF data.pageIndexing THEN IO.PutRope[pageIndexStream, ")\n"]; ENDLOOP; ImagerInterpress.Close[master]; IF data.pageIndexing THEN IO.Close[pageIndexStream]; }; FindFullName: PROC [inputName: ROPE] RETURNS [ROPE] ~ { fullFName: ROPE ¬ NIL; fullFName ¬ FS.FileInfo[inputName].fullFName; RETURN [fullFName] }; ExtendName: PROC [inputName, ext: ROPE] RETURNS [ROPE] ~ { fullFName: ROPE ¬ NIL; cp: FS.ComponentPositions; [fullFName, cp] ¬ FS.ExpandName[inputName]; RETURN [Rope.Cat[Rope.Substr[fullFName, cp.base.start, cp.base.length], ".", ext]]; }; CmdTokenBreak: PROC [char: CHAR] RETURNS [IO.CharClass] = { IF char = '_ THEN RETURN [break]; IF char = ' OR char = '\t OR char = ', OR char = '; OR char = '\n THEN RETURN [sepr]; RETURN [other]; }; GetCmdToken: PROC [stream: IO.STREAM] RETURNS [rope: ROPE] = { rope ¬ NIL; rope ¬ stream.GetTokenRope[CmdTokenBreak ! IO.EndOfStream => CONTINUE].token; rope ¬ FileNames.ResolveRelativePath[rope]; -- Bloomenthal, February 2, 1989 }; ParseError: ERROR [msg: ROPE, index: INT] ~ CODE; RaiseParseError: PROC [stream: IO.STREAM, msg: ROPE] ~ { ERROR ParseError[msg, IO.GetIndex[stream]]; }; ParsedCommand: TYPE ~ REF ParsedCommandRep; ParsedCommandRep: TYPE ~ RECORD [ outputName: ROPE ¬ NIL, inputNames: LIST OF ROPE ¬ NIL, style: ROPE, rootPrefix: ROPE, rootPostfix: ROPE, skipPages: INT, nPages: INT, magnify, rotate: REAL, translate: VEC, verbose: BOOL, screenFormat: BOOL, background: ImagerColor.ConstantColor, version: REAL ¬ 3.0, pageIndexing: BOOL ¬ TRUE, levelClip: INTEGER ¬ 0 ]; ExpandStars: PROC [names: LIST OF ROPE] RETURNS [LIST OF ROPE] ~ { new: LIST OF ROPE ¬ LIST[NIL]; last: LIST OF ROPE ¬ new; NameAction: PROC [fullFName: ROPE] RETURNS [continue: BOOL ¬ TRUE] ~ { last.rest ¬ LIST[fullFName]; last ¬ last.rest; }; FOR p: LIST OF ROPE ¬ names, p.rest UNTIL p = NIL DO r: ROPE ¬ p.first; IF Rope.Find[s1: r, s2: "*"] >= 0 THEN { IF Rope.Find[s1: r, s2: "!"] < 0 THEN r ¬ Rope.Concat[r, "!H"]; FS.EnumerateForNames[r, NameAction] } ELSE [] ¬ NameAction[FindFullName[r]]; ENDLOOP; RETURN [new.rest] }; Parse: PROC [stream: IO.STREAM] RETURNS [data: ParsedCommand] ~ { outputName: ROPE ¬ GetCmdToken[stream]; secondTokenIndex: INT ¬ stream.GetIndex; gets: ROPE ¬ GetCmdToken[stream]; inputNames: LIST OF ROPE ¬ NIL; inputNamesTail: LIST OF ROPE ¬ NIL; keySeen: BOOL ¬ FALSE; IF NOT (gets.Equal["_"] OR gets.Equal["¬"]) THEN { inputNames ¬ inputNamesTail ¬ LIST[outputName]; outputName ¬ NIL; stream.SetIndex[secondTokenIndex]; } ELSE {inputNames ¬ inputNamesTail ¬ LIST[GetCmdToken[stream]]}; IF inputNames = NIL THEN RaiseParseError[stream, usage]; DO index: INT ¬ IO.GetIndex[stream]; name: ROPE ¬ GetCmdToken[stream]; bad: BOOL ¬ FALSE; c: CHAR; IF Rope.Size[name] = 0 THEN EXIT; IF (c ¬ Rope.Fetch[name, 0]) IN ['0..'9] OR c = '- OR c='. OR c='$ THEN bad ¬ TRUE ELSE IF IsKeyword[name] THEN bad ¬ TRUE ELSE name ¬ FS.ExpandName[name ! FS.Error => {bad ¬ TRUE; CONTINUE}].fullFName; IF bad THEN {IO.SetIndex[stream, index]; EXIT}; inputNamesTail.rest ¬ LIST[name]; inputNamesTail ¬ inputNamesTail.rest; ENDLOOP; inputNames ¬ ExpandStars[inputNames]; IF Rope.Size[outputName] = 0 THEN outputName ¬ ExtendName[inputNames.first, "interpress"]; data ¬ NEW[ParsedCommandRep ¬ default­]; data.outputName ¬ outputName; data.inputNames ¬ inputNames; ParseParameters[stream, data]; }; ParamName: TYPE ~ {nil, <> style, prefix, postfix, device, skipPages, nPages, magnify, rotate, translate, version, background, levelClip, in, pt, cm, mm, bp, <> verbose, terse, screenFormat, printFormat, pageIndexing, noPageIndexing }; Keywords: TYPE ~ ParamName[verbose..noPageIndexing]; RopeForParamName: REF ARRAY ParamName OF ROPE ¬ NEW [ ARRAY ParamName OF ROPE ¬ [nil: NIL, style: "style", prefix: "prefix", postfix: "postfix", device: "device", skipPages: "skipPages", nPages: "nPages", magnify: "magnify", rotate: "rotate", translate: "translate", version: "version", background: "background", levelClip: "levelClip", in: "in", pt: "pt", cm: "cm", mm: "mm", bp: "bp", verbose: "verbose", terse: "terse", screenFormat: "screenFormat", printFormat: "printFormat", pageIndexing: "pageIndexing", noPageIndexing: "noPageIndexing"] ]; ParamNameForRefText: PROC [text: REF TEXT] RETURNS [ParamName] ~ TRUSTED { FOR p: ParamName IN (nil..ParamName.LAST] DO IF Rope.Equal[LOOPHOLE[text], RopeForParamName[p], FALSE] THEN RETURN [p] ENDLOOP; RETURN [nil] }; IsKeyword: PROC [name: ROPE] RETURNS [BOOL] ~ { FOR p: ParamName IN Keywords DO IF Rope.Equal[name, RopeForParamName[p], FALSE] THEN RETURN [TRUE] ENDLOOP; RETURN [FALSE] }; ParseParameters: PROC [stream: IO.STREAM, data: ParsedCommand] ~ { stackSize: NAT ~ 3; Type: TYPE ~ {number, dimension, string, negate}; stack: ARRAY [0..stackSize) OF REAL; stringStack: ARRAY [0..stackSize) OF ROPE ¬ ALL[NIL]; stackType: ARRAY [0..stackSize) OF Type ¬ ALL[number]; stackTop: NAT ¬ 0; token: REF TEXT ¬ NEW[TEXT[30]]; tokenKind: IO.TokenKind ¬ tokenERROR; CheckStack: PROC ~ { IF stackTop = stackSize THEN RaiseParseError[stream, "Too many consecutive parameters "]; IF stackTop > 0 AND stackType[stackTop-1] = negate THEN RaiseParseError[stream, "Misplaced minus sign "]; }; CheckN: PROC [size: NAT] ~ { IF stackTop # size THEN RaiseParseError[stream, "Wrong number of parameters "]; }; PopReal: PROC RETURNS [r: REAL ¬ 0] ~ { IF stackTop = 0 THEN RaiseParseError[stream, "Missing parameter "]; IF stackType[stackTop-1] # number THEN RaiseParseError[stream, "Number expected "]; r ¬ stack[stackTop-1]; stackTop ¬ stackTop - 1; }; PopDimn: PROC RETURNS [r: REAL ¬ 0] ~ { IF stackTop = 0 THEN RaiseParseError[stream, "Missing parameter "]; IF stackType[stackTop-1] # dimension THEN RaiseParseError[stream, "Dimension expected "]; r ¬ stack[stackTop-1]; stackTop ¬ stackTop - 1; }; MakeMeters: PROC [multiplier: REAL] ~ { r: REAL ¬ PopReal[]; stack[stackTop] ¬ r*multiplier; stackType[stackTop] ¬ dimension; stackTop ¬ stackTop + 1; }; PopInt: PROC RETURNS [INT] ~ { r: REAL ¬ PopReal[]; i: INT ¬ Real.Round[r]; IF i#r THEN RaiseParseError[stream, "Integer expected "]; RETURN [i] }; GetTok: PROC RETURNS [BOOL] ~ { tokenError: IO.TokenError ¬ none; charsSkipped: INT ¬ 0; [tokenKind: tokenKind, token: token, charsSkipped: charsSkipped, error: tokenError] ¬ IO.GetCedarToken[stream: stream, buffer: token, flushComments: TRUE]; SELECT tokenKind FROM tokenEOF => RETURN [FALSE]; tokenID, tokenDECIMAL, tokenREAL, tokenROPE, tokenATOM, tokenSINGLE => RETURN [TRUE]; tokenERROR, tokenOCTAL, tokenHEX, tokenCHAR, tokenDOUBLE => RaiseParseError[stream, Rope.Concat["Unknown token: ", Rope.FromRefText[token]]]; ENDCASE => ERROR; RETURN [FALSE] }; SetUserProfileOptions[data]; WHILE GetTok[] DO SELECT tokenKind FROM tokenDECIMAL, tokenREAL => TRUSTED { real: REAL ¬ Convert.RealFromRope[LOOPHOLE[token]]; IF stackTop > 0 AND stackType[stackTop-1] = negate THEN { real ¬ -real; stackTop ¬ stackTop-1; }; CheckStack[]; stack[stackTop] ¬ real; stackType[stackTop] ¬ number; stackTop ¬ stackTop + 1; }; tokenSINGLE => { IF token[0] = '- THEN { CheckStack[]; stackType[stackTop] ¬ negate; stackTop ¬ stackTop + 1; } ELSE {RaiseParseError[stream, IO.PutFR1[format: "Illegal token: %g", value: [text[token]]]]}; }; tokenROPE => { CheckStack[]; stringStack[stackTop] ¬ Convert.RopeFromLiteral[Rope.FromRefText[token]]; stackType[stackTop] ¬ string; stackTop ¬ stackTop + 1; }; tokenATOM => { CheckStack[]; IF token[0] # '$ THEN ERROR; stringStack[stackTop] ¬ Rope.FromRefText[s: token, start: 1]; stackType[stackTop] ¬ string; stackTop ¬ stackTop + 1; }; tokenID => { SELECT ParamNameForRefText[token] FROM style => { CheckN[1]; IF stackType[stackTop-1]#string THEN RaiseParseError[stream, "style needs string or atom parameter "]; stackTop ¬ stackTop-1; data.style ¬ stringStack[stackTop]; }; prefix => { CheckN[1]; IF stackType[stackTop-1]#string THEN RaiseParseError[stream, "prefix needs string or atom parameter "]; stackTop ¬ stackTop-1; data.rootPrefix ¬ Rope.Cat[data.rootPrefix, " ", stringStack[stackTop]]; }; postfix => { CheckN[1]; IF stackType[stackTop-1]#string THEN RaiseParseError[stream, "postfix needs string or atom parameter "]; stackTop ¬ stackTop-1; data.rootPostfix ¬ Rope.Cat[data.rootPostfix, " ", stringStack[stackTop]]; }; device => { CheckN[1]; IF stackType[stackTop-1]#string THEN RaiseParseError[stream, "device needs string or atom parameter "]; stackTop ¬ stackTop-1; data.rootPrefix ¬ Rope.Cat[data.rootPrefix, " (", stringStack[stackTop], ") device"]; }; skipPages => {data.skipPages ¬ PopInt[]; CheckN[0]}; nPages => {data.nPages ¬ PopInt[]; CheckN[0]}; magnify => {data.magnify ¬ PopReal[]; CheckN[0]}; rotate => {data.rotate ¬ PopReal[]; CheckN[0]}; translate => {data.translate.y ¬ PopReal[]; data.translate.x ¬ PopReal[]; CheckN[0]}; terse => {data.verbose ¬ FALSE; CheckN[0]}; verbose => {data.verbose ¬ TRUE; CheckN[0]}; version => {data.version ¬ PopReal[]; CheckN[0]}; screenFormat => {data.screenFormat ¬ TRUE; CheckN[0]}; printFormat => {data.screenFormat ¬ FALSE; CheckN[0]}; background => { b: REAL ¬ PopReal[]; g: REAL ¬ PopReal[]; r: REAL ¬ PopReal[]; IF MIN[r, g, b] < 1.0 THEN data.background ¬ ImagerColor.ColorFromRGB[[r, g, b]]; }; levelClip => {data.levelClip ¬ PopInt[]; CheckN[0]}; in => {MakeMeters[0.0254]}; pt => {MakeMeters[0.0254/72.27]}; cm => {MakeMeters[0.01]}; mm => {MakeMeters[0.001]}; bp => {MakeMeters[0.0254/72.0]}; noPageIndexing => {data.pageIndexing ¬ FALSE}; pageIndexing => {data.pageIndexing ¬ TRUE}; ENDCASE => RaiseParseError[stream, "Unknown keyword parameter: "]; }; ENDCASE => ERROR; ENDLOOP; CheckStack[]; CheckN[0]; }; SetUserProfileOptions: PROC [data: ParsedCommand] ~ { data.pageIndexing ¬ UserProfile.Boolean["TiogaToInterpress.PageIndexing", TRUE]; }; Command: Commander.CommandProc ~ { stream: IO.STREAM ¬ IO.RIS[cmd.commandLine]; refAction: REF ActionProc ~ NARROW[cmd.procData.clientData]; backgroundTask: PROC ~ { data: ParsedCommand; data ¬ Parse[stream ! ParseError => { start: INT ¬ MAX[index-10, 0]; cmd.out.PutRope[msg]; IF start > 0 THEN cmd.out.PutRope["..."]; cmd.out.PutRope[cmd.commandLine.Substr[start, index-start]]; IF index > 1 THEN cmd.out.PutRope["..."]; cmd.out.PutRope["\n"]; GOTO Quit }; FS.Error => { cmd.out.PutRope[error.explanation]; cmd.out.PutRope["\n"]; GOTO Quit }; ]; cmd.out.PutRope["Reading "]; cmd.out.PutRope[data.inputNames.first]; cmd.out.PutRope[" . . . "]; refAction­[data, cmd.out ! LocalError => { cmd.out.PutRope[rope]; cmd.out.PutRope["\n"]; GOTO Quit }]; IF data.outputName # NIL THEN { data.outputName ¬ FindFullName[data.outputName]; cmd.out.PutRope[data.outputName]; cmd.out.PutRope[" written.\n"]; } ELSE cmd.out.PutRope[" ok.\n"]; EXITS Quit => {} }; CedarProcess.DoWithPriority[background, backgroundTask]; }; default: ParsedCommand ¬ NEW[ParsedCommandRep ¬ [style: NIL, rootPrefix: NIL, rootPostfix: NIL, skipPages: 0, nPages: 0, magnify: 0.0, rotate: 0.0, translate: [0.0, 0.0], verbose: FALSE, screenFormat: FALSE, background: NIL, version: 3.0, pageIndexing: TRUE, levelClip: 0]]; paramRope: ROPE ~ " \"\" style 0 skipPages 999999 nPages printFormat--screenFormat-- terse--verbose-- 3.0 version 1.0 magnify 0.0 rotate 1.0 1.0 1.0 background"; usage: ROPE ~ " Convert a Tioga file to an Interpress file: TiogaToInterpress [ _] [option*]; options include: style (default: NIL) skipPages (default: 0) nPages (default: 999999) version (default: 3.0> magnify (default: 1.0) rotate (default: 0.0) translate (default: 0.0 0.0) background (default: 1.0 1.0 1.0) (default: printFormat) levelClip (default: none) (default: terse) (default: page indexing)"; ParseParameters[IO.RIS[paramRope], default]; Commander.Register[ "TiogaToInterpress", Command, usage, NEW[ActionProc ¬ TiogaToInterpressAction]]; END. <> <> <>