DIRECTORY Atom USING [GetPName, GetPropFromList, MakeAtom, PropList, PutPropOnList], Commander USING [Handle], Convert USING [RopeFromInt], Imager USING [Box, ConcatT, Context, DoSave, SetFont, SetXY, ShowRope, ShowXChar, TranslateT], ImagerBox USING [BoundingBox, BoxFromExtents, BoxFromRectangle, RectangleFromBox], ImagerFont USING [Amplified, BoundingBox, Font, RopeBoundingBox, RopeEscapement, Escapement, XChar], ImagerTransformation USING [Concat, InverseTransform, Transformation, TransformRectangle, TransformVec], IO USING [PutRope, STREAM], MessageWindow USING [Append], NodeProps USING [GetProp], NodeStyle USING [GetBottomIndent, GetBottomLeading, GetBottomLeadingShrink, GetBottomLeadingStretch, GetColumns, GetFontSize, GetLeading, GetLeadingShrink, GetLeadingStretch, GetReal, GetTopIndent, GetTopLeading, GetTopLeadingShrink, GetTopLeadingStretch, PointsPerFil, RealParam, Ref], NodeStyleFont USING [FontFromStyleParams], NodeStyleOps USING [Alloc, ApplyAll, ApplyFormat, Free, GetStyleParam, OfStyle, nonNumeric], Process USING [CheckForAbort], ProcessProps USING [GetProp], Real USING [LargestNumber, Round], Rope USING [Cat, Concat, Equal, IsEmpty, ROPE, Size], Scaled USING [Float, FromReal], TEditFormat USING [Allocate, FormatLine, LineInfo, Paint, Release, Resolve], TextEdit USING [Size], TextNode USING [Forward, Level, Location, LocNumber, nullLocation, Ref, Root, StepForward], TiogaImager USING [Box, Boxes, BoxRep, Class, ClassRep, FilterProc, Fix, FormattedNodes, FormattedPage, InsertID, SepProc], TJaM USING [Stop], Vector2 USING [Add, Sub, VEC]; TiogaImagerImpl: CEDAR PROGRAM IMPORTS Atom, Convert, Imager, ImagerBox, ImagerFont, IO, Process, ProcessProps, ImagerTransformation, Rope, MessageWindow, NodeProps, NodeStyle, NodeStyleFont, NodeStyleOps, Scaled, TEditFormat, TextEdit, TextNode, TJaM, Vector2 EXPORTS TiogaImager ~ BEGIN Context: TYPE ~ Imager.Context; Font: TYPE ~ ImagerFont.Font; Transformation: TYPE ~ ImagerTransformation.Transformation; VEC: TYPE ~ Vector2.VEC; XChar: TYPE ~ ImagerFont.XChar; ROPE: TYPE ~ Rope.ROPE; Box: TYPE ~ TiogaImager.Box; Boxes: TYPE ~ TiogaImager.Boxes; BoxRep: TYPE ~ TiogaImager.BoxRep; Class: TYPE ~ TiogaImager.Class; ClassRep: TYPE ~ TiogaImager.ClassRep; Fix: TYPE ~ TiogaImager.Fix; FilterProc: TYPE ~ TiogaImager.FilterProc; FormattedNodes: TYPE ~ TiogaImager.FormattedNodes; InsertID: TYPE ~ TiogaImager.InsertID; SepProc: TYPE ~ PROC [InsertID] RETURNS [TiogaImager.Box]; Marks: TYPE ~ Atom.PropList; FormattedPage: TYPE ~ TiogaImager.FormattedPage; FormatLine: PUBLIC PROC [start: TextNode.Location, lineWidth: REAL, style: NodeStyle.Ref, screenStyle: BOOL] RETURNS [box: Box] ~ { lineInfo: TEditFormat.LineInfo _ TEditFormat.Allocate[]; bounds: Imager.Box; xOffset: REAL; Process.CheckForAbort[]; TEditFormat.FormatLine[lineInfo: lineInfo, node: start.node, startOffset: MAX[start.where, 0], nodeStyle: style, lineWidth: Scaled.FromReal[lineWidth], doLigsAndKern: TRUE, kind: IF screenStyle THEN screen ELSE print]; Process.CheckForAbort[]; bounds _ [xmin: lineInfo.xmin, ymin: lineInfo.ymin, xmax: lineInfo.xmax, ymax: lineInfo.ymax]; xOffset _ Scaled.Float[lineInfo.xOffset]; bounds.xmin _ bounds.xmin + xOffset; bounds.xmax _ bounds.xmax + xOffset; box _ NEW[BoxRep _ [ nChars: lineInfo.nChars, bounds: bounds, expansion: [0, 0], escapement: [0, -NodeStyle.GetLeading[style]], stretch: [0, 0], shrink: [0, 0], class: tiogaClass, data: lineInfo ]]; }; InsertIDFromRope: PROC [rope: ROPE] RETURNS [InsertID] ~ { RETURN [SELECT TRUE FROM Rope.Equal[rope, "top", FALSE] => top, Rope.Equal[rope, NIL, FALSE] => normal, Rope.Equal[rope, "bottom", FALSE] => bottom, Rope.Equal[rope, "foot", FALSE] => foot, ENDCASE => nil ] }; Msg: PROC [rope: ROPE] ~ { WITH ProcessProps.GetProp[$CommanderHandle] SELECT FROM cmd: Commander.Handle => { msgStream: IO.STREAM _ cmd.err; IO.PutRope[msgStream, " "]; IO.PutRope[msgStream, rope]; IO.PutRope[msgStream, "\n"]; }; ENDCASE => { MessageWindow.Append["TiogaImagerImpl: ", TRUE]; MessageWindow.Append[rope, FALSE]; }; }; GetStyleParam: PROC [style: NodeStyle.Ref, param: ATOM, default: REAL, kind: NodeStyleOps.OfStyle] RETURNS [REAL] ~ { val: REAL _ default; val _ NodeStyleOps.GetStyleParam[s: style, name: param, styleName: style.name[style], kind: kind ! TJaM.Stop, NodeStyleOps.nonNumeric => {Msg[Rope.Concat["Undefined StyleParam: ", Atom.GetPName[param]]]; CONTINUE}]; RETURN [val] }; FormatNodes: PUBLIC PROC [start: TextNode.Location, bounds: VEC, screenStyle: BOOL, filter: FilterProc, sep: TiogaImager.SepProc] RETURNS [FormattedNodes] ~ { loc: TextNode.Location _ start; level: INT _ TextNode.Level[loc.node]; nodeSize: INT _ TextEdit.Size[loc.node]; kind: NodeStyleOps.OfStyle ~ IF screenStyle THEN screen ELSE print; nodeStyle: NodeStyle.Ref _ GetNodeStyle[loc.node, kind]; insertID: InsertID _ InsertIDFromRope[NARROW[NodeProps.GetProp[loc.node, $Insert]]]; NextNode: PROC ~ { nx: TextNode.Ref; levelDelta: INTEGER; [nx, levelDelta] _ TextNode.Forward[loc.node]; loc.node _ nx; loc.where _ 0; level _ level + levelDelta; nodeSize _ TextEdit.Size[loc.node]; IF loc.node # NIL THEN { NodeStyleOps.ApplyAll[nodeStyle, loc.node, kind]; insertID _ InsertIDFromRope[NARROW[NodeProps.GetProp[loc.node, $Insert]]]; }; }; boxes: Boxes _ [NIL, NIL]; lastNode: TextNode.Ref _ NIL; hSoFar: REAL _ 0.0; stopper: NAT _ 10000; -- to prevent runaways; will cause ERROR when negative firstTime: BOOL _ TRUE; -- do loop body at least once, to assure progress keepStretch: REAL _ 0.0; box: Box _ NIL; boxToStretch: Box _ NIL; maxVerticalExpansion: REAL _ 0; maxVerticalExpansion _ GetStyleParam[nodeStyle, $maxVerticalExpansion, 5, kind]; hSoFar _ 0; WHILE firstTime OR (loc.node # NIL AND hSoFar <= bounds.y) DO skip, stop: BOOL _ FALSE; xbound: REAL _ Real.LargestNumber; IF filter#NIL THEN [skip, stop, xbound] _ filter[loc, level, [0, -hSoFar]]; IF stop THEN {IF skip OR loc.where = nodeSize THEN NextNode[]; EXIT}; IF NOT skip AND NOT firstTime AND loc.where = 0 THEN { keep: REAL ~ GetStyleParam[nodeStyle, $keep, 0, kind]; IF hSoFar + keep > bounds.y THEN { keepStretch _ GetStyleParam[nodeStyle, $keepStretch, NodeStyle.PointsPerFil, kind]; EXIT; }; }; IF NOT skip THEN { lineBox: Box _ FormatLine[loc, MIN[bounds.x, xbound], nodeStyle, screenStyle]; lineBox.stretch.y _ -NodeStyle.GetLeadingStretch[nodeStyle]; lineBox.shrink.y _ -NodeStyle.GetLeadingShrink[nodeStyle]; stopper _ stopper - 1; IF boxes.list = NIL THEN { topIndent: REAL _ NodeStyle.GetTopIndent[nodeStyle]; topIndentStretch: REAL _ GetStyleParam[nodeStyle, $topIndentStretch, 0, kind]; boxes _ AppendBox[boxes, EmptyBox[escapement: [0, -topIndent], stretch: [0, -topIndentStretch]]]; hSoFar _ hSoFar + topIndent; } ELSE IF loc.where = 0 THEN { prevBox: Box ~ boxes.last.first; prevSkip: REAL ~ -prevBox.escapement.y; newSkip: REAL ~ MAX[prevSkip, NodeStyle.GetTopLeading[nodeStyle]]; IF hSoFar - lineBox.escapement.y + (newSkip-prevSkip) > bounds.y AND NOT firstTime THEN EXIT; hSoFar _ hSoFar + (newSkip-prevSkip); prevBox.escapement.y _ -newSkip; prevBox.shrink.y _ prevBox.shrink.y-NodeStyle.GetTopLeadingShrink[nodeStyle]; prevBox.stretch.y _ prevBox.stretch.y-NodeStyle.GetTopLeadingStretch[nodeStyle]; lineBox.stretch.y _ -NodeStyle.GetLeadingStretch[nodeStyle]; }; IF hSoFar - lineBox.escapement.y > bounds.y AND NOT firstTime THEN EXIT; IF lineBox.nChars = 0 AND nodeSize > 0 THEN ERROR; loc.where _ loc.where + lineBox.nChars; IF loc.where = nodeSize THEN { lineBox.escapement.y _ -NodeStyle.GetBottomLeading[nodeStyle]; lineBox.shrink.y _ -NodeStyle.GetBottomLeadingShrink[nodeStyle]; lineBox.stretch.y _ -NodeStyle.GetBottomLeadingStretch[nodeStyle]; }; boxes _ AppendBox[boxes, lineBox]; lastNode _ loc.node; hSoFar _ hSoFar - lineBox.escapement.y; }; IF skip OR loc.where = nodeSize THEN NextNode[]; firstTime _ FALSE; ENDLOOP; IF loc.node = NIL THEN { keepStretch _ keepStretch + NodeStyle.PointsPerFil; }; IF boxes.last # NIL AND lastNode # NIL THEN { lastBox: Box _ boxes.last.first; NodeStyleOps.ApplyAll[nodeStyle, lastNode, kind]; lastBox.escapement.y _ -NodeStyle.GetBottomIndent[nodeStyle]; lastBox.stretch.y _ -GetStyleParam[nodeStyle, $bottomIndentStretch, 0, kind]; lastBox.stretch.y _ lastBox.stretch.y - keepStretch; boxToStretch _ lastBox; lastBox.shrink.y _ 0; }; box _ BoxFromList[boxes.list, [], [size, -bounds.y]]; IF box.expansion.y > maxVerticalExpansion AND boxToStretch#NIL THEN { boxToStretch.stretch.y _ boxToStretch.stretch.y - NodeStyle.PointsPerFil; box _ BoxFromList[boxes.list, [], [size, -bounds.y]]; }; NodeStyleOps.Free[nodeStyle]; RETURN [[box: box, resume: loc]]; }; GetNodeStyle: PROC [node: TextNode.Ref, kind: NodeStyleOps.OfStyle] RETURNS [nodeStyle: NodeStyle.Ref] ~ { nodeStyle _ NodeStyleOps.Alloc[]; NodeStyleOps.ApplyAll[nodeStyle, node, kind]; }; validMarks: LIST OF ATOM _ LIST[ $insideRectoHeader, $centerRectoHeader, $outsideRectoHeader, $centerVersoHeader, $insideVersoHeader, $outsideVersoHeader, $insideHeader, $centerHeader, $outsideHeader, $insideRectoFooter, $centerRectoFooter, $outsideRectoFooter, $outsideVersoFooter, $centerVersoFooter, $insideVersoFooter, $insideFooter, $centerFooter, $outsideFooter, $headSeparator, $topSeparator, $bottomSeparator, $footSeparator, $headSep, $topSep, $bottomSep, $footSep ]; GetMarkAtom: PROC [mark: ROPE, location: TextNode.Location] RETURNS [ATOM] ~ { atom: ATOM _ NIL; realAtom: ATOM _ NIL; FOR p: LIST OF ATOM _ validMarks, p.rest UNTIL p = NIL DO atom _ p.first; IF Rope.Equal[Atom.GetPName[atom], mark, FALSE] THEN { realAtom _ SELECT atom FROM $headSep => $headSeparator, $topSep => $topSeparator, $bottomSep => $bottomSeparator, $footSep => $footSeparator, ENDCASE => atom; EXIT; }; atom _ NIL; ENDLOOP; IF realAtom = NIL OR realAtom # atom THEN { msg: ROPE _ NIL; IF realAtom = NIL THEN { msg _ msg.Cat["Unrecognized mark property \"", mark, "\""]; } ELSE { msg _ msg.Cat["Obsolete mark property ", Atom.GetPName[atom]]; msg _ msg.Cat[" (Use ", Atom.GetPName[realAtom], ")"]; }; msg _ msg.Cat[" at location ", Convert.RopeFromInt[TextNode.LocNumber[at: location, skipCommentNodes: TRUE]]]; Msg[msg]; }; RETURN [realAtom] }; topVersoItems: LIST OF LIST OF ATOM _ LIST[ LIST[$outsideVersoHeader, $outsideHeader, $PAGE], LIST[$centerVersoHeader, $centerHeader], LIST[$insideVersoHeader, $insideHeader] ]; botVersoItems: LIST OF LIST OF ATOM _ LIST[ LIST[$PAGE, $outsideVersoFooter, $outsideFooter], LIST[$DROPFOLIO, $centerVersoFooter, $centerFooter], LIST[$insideVersoFooter, $insideFooter] ]; topRectoItems: LIST OF LIST OF ATOM _ LIST[ LIST[$insideRectoHeader, $insideHeader], LIST[$centerRectoHeader, $centerHeader], LIST[$outsideRectoHeader, $outsideHeader, $PAGE] ]; botRectoItems: LIST OF LIST OF ATOM _ LIST[ LIST[$insideRectoFooter, $insideFooter], LIST[$DROPFOLIO, $centerRectoFooter, $centerFooter], LIST[$PAGE, $outsideRectoFooter, $outsideFooter] ]; FormatPage: PUBLIC PROC [pageCounter: INT, startLoc: TextNode.Location, filter: FilterProc _ NIL, marks: Marks _ NIL, screenStyle: BOOL _ FALSE] RETURNS [FormattedPage] ~ { kind: NodeStyleOps.OfStyle ~ IF screenStyle THEN screen ELSE print; pageNeedsNumber: BOOL _ TRUE; topMarks: Atom.PropList ~ marks; botMarks: Atom.PropList _ marks; GetSeparator: PROC [i: InsertID] RETURNS [LIST OF Box _ NIL] ~ { key: ATOM ~ SELECT i FROM top => $headSeparator, normal => $topSeparator, bottom => $bottomSeparator, foot => $footSeparator, ENDCASE => NIL; IF key # NIL THEN RETURN [NARROW[Atom.GetPropFromList[botMarks, key]]] }; root: TextNode.Ref ~ TextNode.Root[startLoc.node]; nodeStyle: NodeStyle.Ref _ GetNodeStyle[root, kind]; GetStylePoints: PROC [param: NodeStyle.RealParam] RETURNS [REAL] ~ { RETURN [NodeStyle.GetReal[nodeStyle, param]] }; loc: TextNode.Location _ IF startLoc.node = root THEN [TextNode.StepForward[root], 0] ELSE startLoc; leftMargin: REAL ~ GetStylePoints[leftMargin]; rightMargin: REAL ~ GetStylePoints[rightMargin]; bindingMargin: REAL ~ GetStylePoints[bindingMargin]; topMargin: REAL ~ GetStylePoints[topMargin]; headerMargin: REAL ~ GetStylePoints[headerMargin]; bottomMargin: REAL ~ GetStylePoints[bottomMargin]; footerMargin: REAL ~ GetStylePoints[footerMargin]; nColumns: INT ~ MAX[NodeStyle.GetColumns[nodeStyle], 1]; columnGap: REAL ~ GetStyleParam[nodeStyle, $columnGap, 36, kind]; pageWidth: REAL ~ GetStylePoints[pageWidth]; pageLength: REAL ~ GetStylePoints[pageLength]; totalWidth: REAL ~ MAX[pageWidth-leftMargin-rightMargin-bindingMargin, 0.0]; lineWidth: REAL ~ MAX[(totalWidth-(nColumns-1)*columnGap)/nColumns, 0.0]; pageBodyHeight: REAL ~ MAX[pageLength-topMargin-bottomMargin-headerMargin-footerMargin, 0.0]; bodyHeight: REAL _ pageBodyHeight; firstFolio: INT ~ Real.Round[GetStyleParam[nodeStyle, $firstFolio, 1, kind]]; firstVisibleFolio: INT ~ Real.Round[GetStyleParam[nodeStyle, $firstVisibleFolio, 1, kind]]; lastDropFolio: INT ~ Real.Round[GetStyleParam[nodeStyle, $lastDropFolio, 0, kind]]; twoSided: BOOL ~ GetStyleParam[nodeStyle, $sided, 1.0, kind] > 1.5; firstHeaders: INT ~ Real.Round[GetStyleParam[nodeStyle, $firstHeaders, 0.0, kind]]; processMarksFilter: FilterProc ~ { IF filter # NIL THEN [skip: skip, stop: stop, maxLineLength: maxLineLength] _ filter[node, level, position]; IF NOT skip THEN { filterStartLoc: TextNode.Location _ node; stopOnMarkFilter: FilterProc ~ { IF node # filterStartLoc THEN skip _ stop _ TRUE; }; markProp: ROPE ~ NARROW[NodeProps.GetProp[node.node, $Mark]]; IF NOT markProp.IsEmpty THEN { markAtom: ATOM ~ GetMarkAtom[markProp, filterStartLoc]; inColumn: BOOL ~ SELECT markAtom FROM $headSeparator, $topSeparator, $bottomSeparator, $footSeparator => TRUE ENDCASE => FALSE; width: REAL ~ IF inColumn THEN lineWidth ELSE totalWidth; height: REAL ~ ( SELECT markAtom FROM $insideRectoHeader, $centerRectoHeader, $outsideRectoHeader, $centerVersoHeader, $insideVersoHeader, $outsideVersoHeader, $insideHeader, $centerHeader, $outsideHeader => headerMargin, $insideRectoFooter, $centerRectoFooter, $outsideRectoFooter, $outsideVersoFooter, $centerVersoFooter, $insideVersoFooter, $insideFooter, $centerFooter, $outsideFooter => footerMargin, ENDCASE => pageBodyHeight ); IF markAtom # NIL THEN { IF TextEdit.Size[node.node] = 0 THEN { botMarks _ Atom.PutPropOnList[botMarks, markAtom, NIL]; } ELSE { bx: Box ~ FormatNodes[start: node, bounds: [width, height], screenStyle: screenStyle, filter: stopOnMarkFilter, sep: NIL].box; mark: LIST OF Box ~ UnBox[bx]; Duplicate[bx]; FOR p: LIST OF Box _ mark, p.rest UNTIL p=NIL DO IF p.first = NIL THEN ERROR; Duplicate[p.first]; ENDLOOP; botMarks _ Atom.PutPropOnList[botMarks, markAtom, mark]; }; }; skip _ TRUE; }; }; }; CatMarks: PROC [items: LIST OF LIST OF ATOM] RETURNS [Box] ~ { pageNumber: INT _ pageCounter+firstFolio; boxes: Boxes _ []; result: Box _ NIL; pageKey: ATOM ~ IF pageNumber <= lastDropFolio THEN $DROPFOLIO ELSE $PAGE; FOR p: LIST OF LIST OF ATOM _ items, p.rest UNTIL p=NIL DO b: Box _ NIL; FOR q: LIST OF ATOM _ p.first, q.rest UNTIL b # NIL OR q = NIL DO key: ATOM ~ q.first; IF key = pageKey AND pageNeedsNumber AND pageNumber >= firstVisibleFolio THEN { pageFigure: ROPE ~ Convert.RopeFromInt[pageNumber]; b _ BoxFromRope[pageNumberFont, pageFigure]; b.escapement _ [0, -pageNumberBotIndent]; b _ BoxFromList[LIST[ EmptyBox[escapement: [x: 0, y: -pageNumberTopIndent]], b]]; b.escapement _ [x: b.bounds.xmax, y: 0]; pageNeedsNumber _ FALSE; } ELSE { list: LIST OF Box _ NARROW[Atom.GetPropFromList[botMarks, key]]; IF list#NIL THEN { b _ BoxFromList[list]; Duplicate[b]; b.escapement _ [x: b.bounds.xmax, y: 0]; }; }; ENDLOOP; IF b = NIL THEN b _ EmptyBox[]; b.stretch _ [x: NodeStyle.PointsPerFil, y: 0]; boxes _ AppendBox[boxes, b]; ENDLOOP; IF boxes.last # NIL THEN boxes.last.first.stretch.x _ 0; result _ BoxFromList[list: boxes.list, xFix: [size, totalWidth]]; RETURN [result] }; leftMarg: REAL ~ leftMargin + (IF (NOT twoSided OR ((pageCounter+firstFolio) MOD 2 = 1)) THEN bindingMargin ELSE 0.0); HeadersAndFooters: PROC RETURNS [Box] ~ { topItems: LIST OF LIST OF ATOM _ topRectoItems; botItems: LIST OF LIST OF ATOM _ botRectoItems; b: Box _ EmptyBox[]; IF twoSided THEN SELECT (pageCounter+firstFolio) MOD 2 FROM 0 => {topItems _ topVersoItems; botItems _ botVersoItems}; 1 => {topItems _ topRectoItems; botItems _ botRectoItems}; ENDCASE => NULL; IF (pageCounter+firstFolio) >= firstHeaders THEN { headerBox: Box ~ CatMarks[topItems]; footerBox: Box ~ CatMarks[botItems]; b _ Overlay[b, footerBox, [leftMarg, bottomMargin-footerBox.bounds.ymin]]; b _ Overlay[b, headerBox, [leftMarg, pageLength-topMargin]]; }; RETURN [b]; }; pageList: Boxes _ [NIL, NIL]; pageTitleBox: Box _ NIL; pageBox: Box _ NIL; pageNumberFont: Font _ NIL; pageNumberTopIndent: REAL _ 0; pageNumberBotIndent: REAL _ 0; NodeStyleOps.ApplyFormat[nodeStyle, $pagenumber, $default, kind]; pageNumberFont _ NodeStyleFont.FontFromStyleParams[prefix: nodeStyle.name[fontPrefix], family: nodeStyle.name[fontFamily], face: nodeStyle.fontFace, size: NodeStyle.GetFontSize[nodeStyle], alphabets: nodeStyle.fontAlphabets]; pageNumberTopIndent _ NodeStyle.GetTopIndent[nodeStyle]; pageNumberBotIndent _ NodeStyle.GetBottomIndent[nodeStyle]; IF nColumns > 1 THEN { haveTitle: BOOL _ FALSE; titleFilter: FilterProc ~ { NodeStyleOps.ApplyAll[nodeStyle, node.node, kind]; IF NodeStyle.GetColumns[nodeStyle] # 1 THEN { stop _ TRUE; RETURN }; [skip, stop, maxLineLength] _ processMarksFilter[node, level, position]; IF NOT (skip OR stop) THEN haveTitle _ TRUE; }; formattedTitle: FormattedNodes _ FormatNodes[start: loc, bounds: [totalWidth, bodyHeight], screenStyle: screenStyle, filter: titleFilter, sep: GetSeparator]; IF haveTitle THEN { boxes: LIST OF Box ~ UnBox[formattedTitle.box]; rebox: Box ~ BoxFromList[boxes]; pageTitleBox _ rebox; bodyHeight _ MAX[bodyHeight - ABS[rebox.escapement.y], 0.0]; loc _ formattedTitle.resume; }; }; NodeStyleOps.Free[nodeStyle]; nodeStyle _ NIL; FOR c: INT IN [0..nColumns) WHILE loc.node # NIL DO formatted: FormattedNodes _ FormatColumn[start: loc, bounds: [lineWidth, bodyHeight], screenStyle: screenStyle, filter: processMarksFilter, sep: GetSeparator]; formatted.box.escapement _ [lineWidth+columnGap, 0]; pageList _ AppendBox[pageList, formatted.box]; loc _ formatted.resume; ENDLOOP; pageBox _ BoxFromList[pageList.list]; IF pageTitleBox # NIL THEN { t: Boxes _ [NIL, NIL]; t _ AppendBox[t, pageTitleBox]; t _ AppendBox[t, pageBox]; pageBox _ BoxFromList[t.list]; }; pageBox _ Overlay[HeadersAndFooters[], pageBox, [leftMarg, pageLength-topMargin-headerMargin]]; RETURN [[box: pageBox, nextLoc: loc, marks: botMarks, pageFigure: pageCounter+firstFolio]] }; FormatColumn: PROC [start: TextNode.Location, bounds: VEC, screenStyle: BOOL, filter: FilterProc, sep: TiogaImager.SepProc] RETURNS [FormattedNodes] ~ { loc: TextNode.Location _ start; level: INT _ TextNode.Level[loc.node]; nodeSize: INT _ TextEdit.Size[loc.node]; kind: NodeStyleOps.OfStyle ~ IF screenStyle THEN screen ELSE print; nodeStyle: NodeStyle.Ref _ GetNodeStyle[loc.node, kind]; insertID: InsertID _ InsertIDFromRope[NARROW[NodeProps.GetProp[loc.node, $Insert]]]; NextNode: PROC [newLoc: TextNode.Location] ~ { loc _ newLoc; level _ TextNode.Level[loc.node]; nodeSize _ TextEdit.Size[loc.node]; IF loc.node # NIL THEN { NodeStyleOps.ApplyAll[nodeStyle, loc.node, kind]; insertID _ InsertIDFromRope[NARROW[NodeProps.GetProp[loc.node, $Insert]]]; }; }; NodePropAtom: PROC [loc: TextNode.Location, prop: ATOM] RETURNS [ATOM] ~ { rope: ROPE _ NIL; IF loc.node # NIL THEN rope _ NARROW[NodeProps.GetProp[loc.node, prop]]; RETURN [IF rope.IsEmpty THEN NIL ELSE Atom.MakeAtom[rope]]; }; boxes: ARRAY InsertID OF Boxes _ ALL[[NIL, NIL]]; lastNode: ARRAY InsertID OF TextNode.Ref _ ALL[NIL]; hSoFar: REAL _ 0.0; WHILE loc.node # NIL AND hSoFar <= bounds.y DO skip, stop: BOOL _ FALSE; xbound: REAL _ Real.LargestNumber; IF filter#NIL THEN [skip, stop, xbound] _ filter[loc, level, [0, -hSoFar]]; IF stop THEN {IF skip OR loc.where = nodeSize THEN NextNode[[TextNode.Forward[loc.node].nx, 0]]; EXIT}; IF NOT skip THEN { insertID: InsertID _ InsertIDFromRope[NARROW[NodeProps.GetProp[loc.node, $Insert]]]; floatID: ATOM _ NodePropAtom[loc, $Float]; keepID: ATOM _ NodePropAtom[loc, $Keep]; SameInsert: PROC [loc: TextNode.Location, insertID: InsertID, floatID, keepID: ATOM] RETURNS [BOOL] ~ { IF loc.node = NIL THEN RETURN [FALSE] ELSE RETURN [(floatID = NodePropAtom[loc, $Float]) AND (keepID = NodePropAtom[loc, $Keep]) AND (insertID = InsertIDFromRope[NARROW[NodeProps.GetProp[loc.node, $Insert]]])] }; insertFilter: FilterProc ~ { IF filter # NIL THEN [skip, stop, maxLineLength] _ filter[node, level, position]; IF NOT skip THEN stop _ NOT SameInsert[node, insertID, floatID, keepID]; }; SpaceOccupiedByBoxes: PROC [newID: InsertID] RETURNS [REAL] ~ { hTemp: REAL _ 0.0; HeightOfSep: PROC [id: InsertID] RETURNS [REAL] ~ { h: REAL _ 0.0; b: LIST OF Box _ sep[id]; FOR p: LIST OF Box _ b, p.rest UNTIL p=NIL DO h _ h - p.first.escapement.y; ENDLOOP; RETURN [h]; }; HeightOfBoxes: PROC [id: InsertID] RETURNS [REAL] ~ { h: REAL _ 0.0; FOR p: LIST OF Box _ boxes[id].list, p.rest UNTIL p=NIL DO h _ h - p.first.escapement.y; ENDLOOP; RETURN [h]; }; IF newID = top OR boxes[top].list # NIL THEN hTemp _ hTemp + HeightOfSep[top] + HeightOfBoxes[top] + HeightOfSep[normal]; IF newID = normal OR boxes[normal].list # NIL THEN hTemp _ hTemp + HeightOfBoxes[normal]; IF newID = bottom OR boxes[bottom].list # NIL THEN hTemp _ hTemp + HeightOfSep[bottom] + HeightOfBoxes[bottom]; IF newID = foot OR boxes[foot].list # NIL THEN hTemp _ hTemp + HeightOfSep[foot] + HeightOfBoxes[foot]; RETURN [hTemp]; }; formatted: FormattedNodes _ FormatNodes[start: loc, bounds: [bounds.x, bounds.y - SpaceOccupiedByBoxes[insertID]], screenStyle: screenStyle, filter: insertFilter, sep: sep]; IF SameInsert[formatted.resume, insertID, floatID, keepID] AND SpaceOccupiedByBoxes[nil] # 0.0 THEN { EXIT; }; formatted.box _ BoxFromList[UnBox[formatted.box]]; boxes[insertID] _ AppendBox[boxes[insertID], formatted.box]; loc _ formatted.resume; }; IF skip THEN loc _ [TextNode.Forward[loc.node].nx, 0]; NextNode[loc]; ENDLOOP; { result: Boxes _ [NIL, NIL]; boxToStretch: Box; box: Box; maxVerticalExpansion: REAL _ GetStyleParam[nodeStyle, $maxVerticalExpansion, 5, kind]; AppendSep: PROC [bxs: Boxes, id: InsertID] RETURNS [Boxes] ~ { b: LIST OF Box _ sep[id]; FOR p: LIST OF Box _ b, p.rest UNTIL p=NIL DO Duplicate[p.first]; bxs _ AppendBox[bxs, p.first]; ENDLOOP; RETURN [bxs]; }; IF boxes[top].list # NIL THEN { result _ AppendSep[result, top]; result _ AppendList[result, boxes[top].list]; result _ AppendSep[result, normal]; }; IF boxes[normal].list # NIL THEN { result _ AppendList[result, boxes[normal].list]; boxToStretch _ result.last.first; boxToStretch.stretch.y _ boxToStretch.stretch.y - NodeStyle.PointsPerFil; }; IF boxes[bottom].list # NIL THEN { result _ AppendSep[result, bottom]; result _ AppendList[result, boxes[bottom].list]; }; IF boxes[foot].list # NIL THEN { result _ AppendSep[result, foot]; result _ AppendList[result, boxes[foot].list]; }; box _ BoxFromList[result.list, [], [size, -bounds.y]]; IF box.expansion.y > maxVerticalExpansion AND boxToStretch#NIL THEN { boxToStretch.stretch.y _ boxToStretch.stretch.y - NodeStyle.PointsPerFil; box _ BoxFromList[result.list, [], [size, -bounds.y]]; }; NodeStyleOps.Free[nodeStyle]; RETURN [[box: box, resume: loc]]; }; }; tiogaClass: Class _ NEW[ClassRep _ [ Composite: NIL, UnBox: NIL, Render: TiogaRender, Resolve: TiogaResolve, Destroy: TiogaDestroy ]]; TiogaRender: PROC [box: Box, context: Context, position: VEC] ~ { lineInfo: TEditFormat.LineInfo ~ NARROW[box.data]; position.x _ position.x + Scaled.Float[lineInfo.xOffset]; Imager.SetXY[context, position]; TEditFormat.Paint[lineInfo, context]; }; realInteger: REAL _ LAST[INTEGER]; TiogaResolve: PROC [box: Box, p: VEC] RETURNS [loc: TextNode.Location _ TextNode.nullLocation] ~ { lineInfo: TEditFormat.LineInfo ~ NARROW[box.data]; IF NOT InBox[p, box] THEN RETURN; loc _ TEditFormat.Resolve[lineInfo, Real.Round[MIN[MAX[p.x, -realInteger], realInteger]]].loc; }; TiogaDestroy: PROC [box: Box] ~ { lineInfo: TEditFormat.LineInfo ~ NARROW[box.data]; box.data _ NIL; TEditFormat.Release[lineInfo]; }; EmptyBox: PUBLIC PROC [escapement: VEC _ [0,0], stretch: VEC _ [0,0], shrink: VEC _ [0,0]] RETURNS [box: Box] ~ { box _ NEW[BoxRep _ [ nChars: 0, bounds: [0, 0, 0, 0], expansion: [0, 0], escapement: escapement, stretch: stretch, shrink: shrink, class: emptyClass, data: NIL ]]; }; emptyClass: Class _ NEW[ClassRep _ [ UnBox: NIL, Render: NIL, Resolve: NIL, Destroy: NIL ]]; BoxFromChar: PUBLIC PROC [font: Font, char: CHAR] RETURNS [Box] ~ { RETURN [BoxFromXChar[font, [0, ORD[char]]]] }; BoxFromXChar: PUBLIC PROC [font: Font, xchar: XChar] RETURNS [box: Box] ~ { w: VEC _ ImagerFont.Escapement[font, xchar]; flex: VEC _ IF ImagerFont.Amplified[font, xchar] THEN w ELSE [0, 0]; box _ NEW[BoxRep _ [ nChars: 1, bounds: ImagerBox.BoxFromExtents[ImagerFont.BoundingBox[font, xchar]], expansion: [0, 0], escapement: w, stretch: flex, shrink: flex, class: xCharClass, data: NEW[XCharBoxDataRep _ [font, xchar]] ]]; }; XCharBoxDataRep: TYPE ~ RECORD [font: Font, xchar: XChar]; xCharClass: Class _ NEW[ClassRep _ [ UnBox: NIL, Render: XCharRender, Resolve: NIL, Destroy: NIL ]]; XCharRender: PROC [box: Box, context: Context, position: VEC] ~ { data: REF XCharBoxDataRep ~ NARROW[box.data]; Imager.SetFont[context, data.font]; Imager.SetXY[context, position]; Imager.ShowXChar[context, data.xchar]; }; BoxFromRope: PUBLIC PROC [font: Font, rope: Rope.ROPE] RETURNS [box: Box] ~ { box _ NEW[BoxRep _ [ nChars: Rope.Size[rope], bounds: ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[font, rope]], expansion: [0, 0], escapement: ImagerFont.RopeEscapement[font, rope], stretch: [0, 0], shrink: [0, 0], class: ropeClass, data: NEW[RopeBoxDataRep _ [font, rope]] ]]; }; RopeBoxDataRep: TYPE ~ RECORD [font: Font, rope: ROPE]; ropeClass: Class _ NEW[ClassRep _ [ Composite: NIL, UnBox: NIL, Render: RopeRender, Resolve: NIL, Destroy: NIL ]]; RopeRender: PROC [box: Box, context: Context, position: VEC] ~ { data: REF RopeBoxDataRep ~ NARROW[box.data]; Imager.SetFont[context, data.font]; Imager.SetXY[context, position]; Imager.ShowRope[context, data.rope]; }; CalculateExpansion: PROC [fix: Fix, w, stretch, shrink: REAL] RETURNS [e: REAL] ~ { IF fix.what = expansion THEN e _ fix.value ELSE { denom: REAL _ IF fix.value = 0 OR w/fix.value > 1.0 THEN shrink ELSE stretch; e _ IF ABS[denom] > 1.0e-10 THEN (fix.value-w)/denom ELSE 0.0; }; IF e < -1 THEN e _ -1; }; Advance: PROC [w: VEC, by: Box, expansion: VEC] RETURNS [VEC]~ { w _ w.Add[by.escapement]; SELECT expansion.x FROM = 0.0 => NULL; < 0.0 => w.x _ w.x + expansion.x*by.shrink.x; > 0.0 => w.x _ w.x + expansion.x*by.stretch.x; ENDCASE => NULL; SELECT expansion.y FROM = 0.0 => NULL; < 0.0 => w.y _ w.y + expansion.y*by.shrink.y; > 0.0 => w.y _ w.y + expansion.y*by.stretch.y; ENDCASE => NULL; RETURN [w]; }; CopyList: PROC [list: LIST OF Box] RETURNS [LIST OF Box] ~ { boxes: Boxes _ [NIL, NIL]; FOR p: LIST OF Box _ list, list.rest UNTIL p = NIL DO Duplicate[p.first]; boxes _ AppendBox[boxes, p.first]; ENDLOOP; RETURN [boxes.list]; }; BoxFromList: PUBLIC PROC [list: LIST OF Box, xFix: Fix _ [], yFix: Fix _ []] RETURNS [box: Box] ~ { w: VEC _ [0, 0]; min: VEC _ [Real.LargestNumber, Real.LargestNumber]; max: VEC _ [-Real.LargestNumber, -Real.LargestNumber]; stretch: VEC _ [0, 0]; shrink: VEC _ [0, 0]; expansion: VEC _ [0, 0]; FOR p: LIST OF Box _ list, p.rest UNTIL p=NIL DO w _ w.Add[p.first.escapement]; stretch _ stretch.Add[p.first.stretch]; shrink _ shrink.Add[p.first.shrink]; ENDLOOP; expansion _ [CalculateExpansion[xFix, w.x, stretch.x, shrink.x], CalculateExpansion[yFix, w.y, stretch.y, shrink.y]]; w _ [0, 0]; FOR p: LIST OF Box _ list, p.rest UNTIL p=NIL DO min.x _ MIN[min.x, w.x + p.first.bounds.xmin]; min.y _ MIN[min.y, w.y + p.first.bounds.ymin]; max.x _ MAX[max.x, w.x + p.first.bounds.xmax]; max.y _ MAX[max.y, w.y + p.first.bounds.ymax]; w _ Advance[w, p.first, expansion]; ENDLOOP; IF list = NIL THEN min _ max _ [0, 0]; IF xFix.what = size THEN w.x _ xFix.value; IF yFix.what = size THEN w.y _ yFix.value; box _ NEW[BoxRep _ [ nChars: 0, bounds: [xmin: min.x, ymin: min.y, xmax: max.x, ymax: max.y], expansion: expansion, escapement: w, stretch: [0, 0], shrink: [0, 0], class: listBoxClass, data: list ]]; }; listBoxClass: Class _ NEW[ClassRep _ [ Composite: NIL, UnBox: ListUnBox, Render: ListRender, Resolve: ListResolve, Destroy: ListDestroy ]]; ListUnBox: PROC [box: Box] RETURNS [list: LIST OF Box] ~ { list _ NARROW[box.data]; }; ListRender: PROC [box: Box, context: Context, position: VEC] ~ { w: VEC _ position; expansion: VEC _ box.expansion; FOR p: LIST OF Box _ NARROW[box.data], p.rest UNTIL p=NIL DO Render[p.first, context, w]; w _ Advance[w, p.first, expansion]; ENDLOOP; }; InBox: PROC [p: VEC, box: Box] RETURNS [BOOL] ~ { RETURN [p.x IN [box.bounds.xmin..box.bounds.xmax] AND p.y IN [box.bounds.ymin..box.bounds.ymax]] }; ListResolve: PROC [box: Box, p: VEC] RETURNS [loc: TextNode.Location _ TextNode.nullLocation] ~ { w: VEC _ [0, 0]; expansion: VEC _ box.expansion; list: LIST OF Box _ NARROW[box.data]; IF NOT InBox[p, box] THEN RETURN; list _ NARROW[box.data]; FOR l: LIST OF Box _ list, l.rest UNTIL l=NIL DO pRel: VEC ~ p.Sub[w]; loc _ Resolve[l.first, pRel]; IF loc # TextNode.nullLocation THEN RETURN; w _ Advance[w, l.first, expansion]; ENDLOOP; }; ListDestroy: PROC [box: Box] ~ { list: LIST OF Box _ NARROW[box.data]; rest: LIST OF Box _ NIL; box.data _ NIL; FOR l: LIST OF Box _ list, rest UNTIL l=NIL DO Destroy[l.first]; rest _ l.rest; l.first _ NIL; l.rest _ NIL; ENDLOOP; }; Overlay: PUBLIC PROC [base, new: Box, offset: VEC] RETURNS [Box] ~ { data: OverlayData ~ NEW[OverlayDataRep _ [base, new, offset]]; RETURN [NEW[BoxRep _ [ nChars: 0, bounds: ImagerBox.BoundingBox[base.bounds, new.bounds], expansion: [0,0], escapement: base.escapement, stretch: base.stretch, shrink: base.shrink, class: overlayBoxClass, data: data ]]]; }; overlayBoxClass: Class _ NEW[ClassRep _ [ Composite: NIL, UnBox: NIL, Render: OverlayRender, Resolve: OverlayResolve, Destroy: OverlayDestroy ]]; OverlayData: TYPE ~ REF OverlayDataRep; OverlayDataRep: TYPE ~ RECORD [base, new: Box, offset: VEC]; OverlayRender: PROC [box: Box, context: Context, position: VEC] ~ { data: OverlayData ~ NARROW[box.data]; Render[data.base, context, position]; Render[data.new, context, Vector2.Add[position, data.offset]]; }; OverlayResolve: PROC [box: Box, p: VEC] RETURNS [loc: TextNode.Location _ TextNode.nullLocation] ~ { data: OverlayData ~ NARROW[box.data]; loc _ Resolve[data.new, Vector2.Sub[p, data.offset]]; IF loc.node = NIL THEN loc _ Resolve[data.base, p]; }; OverlayDestroy: PROC [box: Box] ~ { data: OverlayData ~ NARROW[box.data]; Destroy[data.new]; Destroy[data.base]; data.new _ NIL; data.base _ NIL; box.data _ NIL; }; Composite: PUBLIC PROC [box: Box] RETURNS [BOOL] ~ { RETURN [IF box.class.Composite = NIL THEN (box.class.UnBox # NIL) ELSE box.class.Composite[box]] }; UnBox: PUBLIC PROC [box: Box] RETURNS [list: LIST OF Box] ~ { list _ IF box.class.UnBox # NIL THEN box.class.UnBox[box] ELSE NIL; }; ModifyBox: PUBLIC PROC [box: Box, m: Transformation] RETURNS [new: Box] ~ { data: REF ModifiedBoxDataRep ~ NEW[ModifiedBoxDataRep _ [box, m]]; WITH box.data SELECT FROM modified: REF ModifiedBoxDataRep => { data.box _ modified.box; data.m _ ImagerTransformation.Concat[modified.m, m]; }; ENDCASE => NULL; new _ NEW[BoxRep _ [ nChars: box.nChars, bounds: ImagerBox.BoxFromRectangle[ImagerTransformation.TransformRectangle[m, ImagerBox.RectangleFromBox[box.bounds]]], expansion: box.expansion, escapement: ImagerTransformation.TransformVec[m, box.escapement], stretch: ImagerTransformation.TransformVec[m, box.stretch], shrink: ImagerTransformation.TransformVec[m, box.shrink], class: modifiedBoxClass, data: NEW[ModifiedBoxDataRep _ [box, m]] ]]; }; ModifiedBoxDataRep: TYPE ~ RECORD [box: Box, m: Transformation]; modifiedBoxClass: Class _ NEW[ClassRep _ [ Composite: ModifiedComposite, UnBox: ModifiedUnBox, Render: ModifiedRender, Resolve: ModifiedResolve, Destroy: ModifiedDestroy ]]; ModifiedComposite: PROC [box: Box] RETURNS [BOOL] ~ { modified: REF ModifiedBoxDataRep ~ NARROW[box.data]; RETURN [Composite[modified.box]] }; ModifiedUnBox: PROC [box: Box] RETURNS [LIST OF Box] ~ { modified: REF ModifiedBoxDataRep ~ NARROW[box.data]; m: Transformation ~ modified.m; b: Boxes _ [NIL, NIL]; unmodified: LIST OF Box _ UnBox[modified.box]; FOR p: LIST OF Box _ unmodified, p.rest UNTIL p = NIL DO b _ AppendBox[b, ModifyBox[p.first, m]]; ENDLOOP; RETURN [b.list]; }; ModifiedRender: PROC [box: Box, context: Context, position: VEC] ~ { modified: REF ModifiedBoxDataRep ~ NARROW[box.data]; proc: PROC ~ { Imager.TranslateT[context, position]; Imager.ConcatT[context, modified.m]; Render[modified.box, context, [0, 0]]; }; Imager.DoSave[context, proc]; }; ModifiedResolve: PROC [box: Box, p: VEC] RETURNS [loc: TextNode.Location _ TextNode.nullLocation] ~ { modified: REF ModifiedBoxDataRep ~ NARROW[box.data]; RETURN [Resolve[modified.box, ImagerTransformation.InverseTransform[modified.m, p]]] }; ModifiedDestroy: PROC [box: Box] ~ { modified: REF ModifiedBoxDataRep ~ NARROW[box.data]; Destroy[modified.box]; modified.box _ NIL; }; Render: PUBLIC PROC [box: Box, context: Context, position: VEC] ~ { IF box.class.Render # NIL THEN box.class.Render[box, context, position]; }; Resolve: PUBLIC PROC [box: Box, p: VEC] RETURNS [loc: TextNode.Location] ~ { loc _ IF box.class.Resolve # NIL THEN box.class.Resolve[box, p] ELSE TextNode.nullLocation; }; FirstLocWithin: PUBLIC PROC [box: Box] RETURNS [loc: TextNode.Location] ~ { loc _ IF box.class.FirstLocWithin # NIL THEN box.class.FirstLocWithin[box] ELSE TextNode.nullLocation; }; doDestroy: BOOL _ TRUE; Destroy: PUBLIC PROC [box: Box] ~ { IF doDestroy AND box.class # NIL AND box.class.Destroy # NIL AND NOT box.duplicate THEN box.class.Destroy[box]; }; Duplicate: PUBLIC PROC [box: Box] ~ { IF box # NIL THEN box.duplicate _ TRUE; }; AppendList: PUBLIC PROC [boxes: Boxes, list: LIST OF Box] RETURNS [Boxes] ~ { IF list = NIL THEN RETURN [boxes] ELSE IF boxes.list = NIL THEN boxes.list _ list ELSE { IF boxes.last.rest # NIL THEN ERROR; boxes.last.rest _ list; }; UNTIL list.rest = NIL DO list _ list.rest ENDLOOP; boxes.last _ list; RETURN [boxes]; }; AppendBox: PUBLIC PROC [boxes: Boxes, box: Box] RETURNS [Boxes] ~ { list: LIST OF Box _ LIST[box]; IF boxes.list = NIL THEN boxes _ [list: list, last: list] ELSE { IF boxes.last.rest # NIL THEN ERROR; boxes.last.rest _ list; boxes.last _ list; }; RETURN [boxes]; }; END. LTiogaImagerImpl.mesa Copyright Σ 1985, 1986 by Xerox Corporation. All rights reserved. Michael Plass, December 30, 1986 9:33:02 am PST Rick Beach, May 17, 1987 2:10:36 pm PDT N.B. hSoFar is the non-negative height of the page built so far. Note that since the y escapements, and glue are normally negative, we need to subtract them. Check for keep processing. This is the first thing in this insert. Put the appropriate topIndent and topIndentStretch. This is the first line of a node, but not the first thing in this insert. Adjust the topLeading accordingly. For the rest of the lines in the node, the leading is already correct. IF node.node.formatName = $pagebreak THEN skip _ stop _ TRUE; N.B. hSoFar is the non-negative height of the page built so far. Note that since the y escapements, and glue are normally negative, we need to subtract them. measure height of boxes with separators, as if newID insertion was included insert didn't fit within bounds and was not the only content of the page, so force break and try again in the next column insert separators and boxes if this node floats, then box the floating insert, stopping when the pair changes if the box does not fit the current bounds, then add box to floatingInserts mark list and continue with next node <> else box the next line of this node <> floatID: ATOM ~ FloatAtom[loc]; filterFloat: FilterProc ~ { stop _ (floatID = FloatAtom[node]) AND (insertID = InsertIDFromRope[NARROW[NodeProps.GetProp[node.node, $Insert]]]); }; IF floatID # NIL AND filter # filterFloat THEN { format a floating insert only if it has a float id and we are not already formatting one nodes: FormattedNodes _ FormatNodes[start: loc, bounds: [bounds.x, bounds.y - hSoFar], screenStyle: screenStyle, filter: filterFloat, sep: NIL]; IF floatID = FloatAtom[nodes.resume] THEN { -- float did not fit, force page break forceBreak _ TRUE; EXIT; } ELSE lineBox _ nodes.box; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _ &td _ PutGet.FromFile["TiogaDoc.Tioga"] _ &c _ ImagerTerminal.BWContext[Terminal.Current[], TRUE] _ &b _ TiogaImager.FormatNodes[[&td, 0], [400, 600], TRUE, NIL] _ TiogaImager.Render[&b.box, &c, [10, 700]] Κ.w˜code™KšœB™BK™/K™'—K™šΟk ˜ Kšœœ@˜JKšœ œ ˜Kšœœ˜KšœœR˜^Kšœ œC˜RKšœ œT˜dKšœœN˜hKšœœ œ˜Kšœœ ˜Kšœ œ ˜Kšœ œ˜žKšœœ˜*Kšœ œJ˜\Kšœœ˜Kšœ œ ˜Kšœœ˜"Kšœœœ˜5Kšœœ˜Kšœ œ;˜LKšœ œ˜Kšœ œM˜[Kšœ œj˜{Kšœœ˜Kšœœ œ˜—K˜KšΠlnœ ˜Kšœ/œ­˜εKšœ ˜šœ˜K˜Kšœ œ˜Kšœœ˜Kšœœ'˜;Kšœœ œ˜Kšœœ˜šœœœ˜K˜—Kšœœ˜Kšœœ˜ Kšœœ˜"Kšœœ˜ Kšœ œ˜&Kšœœ˜Kšœ œ˜*Kšœœ˜2Kšœ œ˜&Kšœ œœ œ˜:Kšœœ˜Kšœœ˜0K˜š Οn œœœ'œ%œœ˜ƒKšœ8˜8K˜Kšœ œ˜Kšœ˜Kš œJœZœœ œœ˜ΪKšœ˜Kšœ^˜^Kšœ)˜)Kšœ$˜$Kšœ$˜$šœœ ˜Kšœ˜Kšœ˜Kšœ˜Kšœ.˜.Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜K˜—šŸœœœœ˜:šœœœ˜Kšœœ ˜&Kšœœœ ˜'Kšœœ ˜,Kšœœ ˜(Kšœ˜Kšœ˜—Kšœ˜K˜—šŸœœœ˜šœ(œ˜7˜Kšœ œœ ˜Kšœ˜Kšœ˜Kšœ˜K˜—šœ˜ Kšœ*œ˜0Kšœœ˜"Kšœ˜——Kšœ˜K˜—š Ÿ œœœ œœœ˜uKšœœ ˜KšœΜœ˜ΧKšœ˜ Kšœ˜K˜—š Ÿ œœœ$œœ0œ˜žK•StartOfExpansionΰ[lineInfo: TEditFormat.LineInfo, node: TextEdit.RefTextNode, startOffset: TextEdit.Offset, nodeStyle: NodeStyle.Ref, lineWidth: Scaled.Value, doLigsAndKern: BOOLEAN _ FALSE, kind: NodeStyleOps.OfStyle _ screen]šœ˜Kšœœ˜&Kšœ œ˜(Kšœœ œœ˜CKšœ8˜8Kšœ&œ(˜TšŸœœ˜Kšœ˜Kšœ œ˜Kšœ.˜.Kšœ˜Kšœ˜Kšœ˜Kšœ#˜#šœ œœ˜Kšœ1˜1Kšœœ(˜JKšœ˜—Kšœ˜—Kšœœœ˜Kšœœ˜šœœ˜KšΟbœš™ž—Kšœ œ Οc6˜LKšœ œœ‘1˜IKšœ œ˜Kšœ œ˜Kšœœ˜Kšœœ˜KšœP˜PKšœ ˜ š œ œ œœ˜=Kšœ œœ˜Kšœœ˜"Kšœœœ9˜KKš œœœœœ œ˜Eš œœœœ œœ˜6K™Kšœœ,˜6šœœ˜"KšœS˜SKšœ˜K˜—Kšœ˜—šœœœ˜Kšœœ,˜NKšœ<˜˜>Kšœ@˜@KšœB˜BKšœ˜—Kšœ"˜"Kšœ˜Kšœ'˜'Kšœ˜—Kšœœœ ˜0Kšœ œ˜Kšœ˜—šœ œœ˜Kšœ3˜3Kšœ˜—š œœœ œœ˜-Kšœ ˜ Kšœ1˜1Kšœ=˜=KšœM˜MKšœ4˜4Kšœ˜Kšœ˜Kšœ˜—Kšœ5˜5šœ(œœœ˜EKšœI˜IKšœ5˜5Kšœ˜—Kšœ˜Kšœ˜!Kšœ˜K˜—šŸ œœ2œ˜jK˜!Kšœ-˜-Kšœ˜K˜—š œ œœœœ˜ Kšœ<˜˜>Kšœ6˜6Kšœ˜——Kšœfœ˜nK˜ Kšœ˜—Kšœ ˜Kšœ˜K˜—š œœœœœœœ˜+Kšœ-˜1Kšœ$˜(Kšœ#˜'Kšœ˜K˜—š œœœœœœœ˜+Kšœ-˜1Kšœ0˜4Kšœ#˜'Kšœ˜K˜—š œœœœœœœ˜+Kšœ$˜(Kšœ$˜(Kšœ,˜0Kšœ˜K˜—š œœœœœœœ˜+Kšœ$˜(Kšœ0˜4Kšœ,˜0Kšœ˜K˜—šŸ œœœœ4œœœœœ˜¬Kšœœ œœ˜CKšœœœ˜Kšœ ˜ Kšœ ˜ š Ÿ œœœœœœ˜@Kš œœœœeœœ˜Kš œœœœœ&˜FKšœ˜—Kšœ2˜2Kšœ4˜4šŸœœœœ˜DKšœ&˜,Kšœ˜—K–o[node: TextEdit.RefTextNode, name: ROPE, event: TextEdit.Event _ NIL, root: TextEdit.RefTextNode _ NIL]šœœœ!œ ˜dKšœ œ˜.Kšœ œ˜0Kšœœ!˜4Kšœ œ˜,Kšœœ ˜2Kšœœ ˜2Kšœœ ˜2Kšœ œœ%˜8Kšœ œ2˜AKšœ œ˜,Kšœ œ˜.Kšœ œœ6˜LKšœ œœ4˜IKšœœœC˜]Kšœ œ˜"Kšœ œ>˜MKšœœE˜[KšœœA˜SKšœ œ5˜CKšœœB˜SšŸœ˜"šœ œ˜KšœW˜W—šœœœ˜Kšœ)˜)šŸœ˜ Kšœœœ˜1Kšœ˜—Kšœ œœ&˜=Kšœ#œœ™=šœœœ˜Kšœ œ)˜7šœ œ˜Kš œ œDœœœ˜n—Kš œœœ œ œ ˜9šœœ˜šœ ˜Kšœ<˜Kšœ œ˜)Kšœ˜Kšœœ˜Kš œ œœœ œ˜Jšœœœœœœœœ˜:Kšœ œ˜ šœœœœœœœœ˜AKšœœ ˜šœœœ ˜Hšœ˜Kšœ œ#˜3Kšœ,˜,Kšœ)˜)šœœ˜Kšœ6˜6Kšœ˜—Kšœ(˜(Kšœœ˜Kšœ˜—šœ˜Kšœœœœ&˜@šœœœ˜Kšœ˜Kšœ ˜ Kšœ(˜(Kšœ˜—Kšœ˜——Kšœ˜—Kšœœœ˜Kšœ.˜.Kšœ˜Kšœ˜—Kšœœœ ˜8KšœA˜AKšœ ˜Kšœ˜—Kšœ œœœ œœ œœ˜všŸœœœ ˜)Kš œ œœœœœ˜/Kš œ œœœœœ˜/K˜š œ œœœ˜;Kšœ:˜:Kšœ:˜:Kšœœ˜—šœ*œ˜2Kšœ$˜$K–j[start: TextNode.Location, bounds: VEC, screenStyle: BOOL _ FALSE, filter: TiogaImager.FilterProc]šœ$˜$K–j[start: TextNode.Location, bounds: VEC, screenStyle: BOOL _ FALSE, filter: TiogaImager.FilterProc]šœJ˜JKšœ<˜Kšœœœ˜š œœœœœ˜-Kšœ˜Kšœ˜Kšœ˜—Kšœ˜ Kšœ˜—šœœœ˜Kšœ ˜ Kšœ-˜-Kšœ#˜#Kšœ˜—šœœœ˜"Kšœ0˜0Kšœ!˜!KšœI˜IKšœ˜—šœœœ˜"Kšœ#˜#Kšœ0˜0Kšœ˜—šœœœ˜ Kšœ!˜!Kšœ.˜.Kšœ˜—Kšœ6˜6šœ(œœœ˜EKšœI˜IKšœ6˜6Kšœ˜—Kšœ˜Kšœ˜!Kšœ˜—K˜—šœ™™K™I™+™K™@K™-———™K™K™\——Kšœ œ™šŸ œ™Kšœ#œœ*™tK™—šœ œœœ™0KšœX™XK–~[start: TextNode.Location, bounds: VEC, screenStyle: BOOL, filter: TiogaImager.FilterProc, sep: TiogaImager.SepProc]šœ‹œ™šœ#œ‘&™RKšœ œ™Kšœ™Kšœ™—Kšœ™Kšœ™—™WK˜—šœœ ˜$Kšœ œ˜Kšœœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—šŸ œœ(œ˜AKšœ!œ ˜2Kšœ9˜9Kšœ ˜ Kšœ%˜%Kšœ˜K˜—Kšœ œœœ˜"šŸ œœœœ5˜bKšœ!œ ˜2Kšœœœœ˜!Kšœ/œœ(˜^Kšœ˜K˜—šŸ œœ˜!Kšœ!œ ˜2Kšœ œ˜Kšœ˜Kšœ˜K˜—šŸœœœœœœ œ˜qšœœ ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜ Kšœ˜—Kšœ˜K˜—šœœ ˜$Kšœœ˜ Kšœœ˜ Kšœ œ˜ Kšœ ˜ Kšœ˜K˜—š Ÿ œœœœœ ˜CKšœœ ˜+Kšœ˜K˜—šŸ œœœœ˜KKšœœ&˜,Kš œœœ#œœ˜Dšœœ ˜Kšœ ˜ KšœF˜FKšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœœ!˜*Kšœ˜—Kšœ˜K˜—Kšœœœ˜:šœœ ˜$Kšœœ˜ Kšœ˜Kšœ œ˜ Kšœ ˜ Kšœ˜K˜—šŸ œœ(œ˜AKšœœœ ˜-Kšœ#˜#Kšœ ˜ Kšœ&˜&Kšœ˜K˜—š Ÿ œœœœœ˜Mšœœ ˜Kšœ˜KšœI˜IKšœ˜Kšœ2˜2Kšœ˜Kšœ˜K˜Kšœœ˜(Kšœ˜—Kšœ˜K˜—šœœœœ˜7K˜—šœœ ˜#Kšœ œ˜Kšœœ˜ Kšœ˜Kšœ œ˜ Kšœ ˜ Kšœ˜K˜—šŸ œœ(œ˜@Kšœœœ ˜,Kšœ#˜#Kšœ ˜ Kšœ$˜$Kšœ˜K˜—š Ÿœœ œœœ˜SKšœœ˜*šœ˜Kš œœœœœœ ˜MKš œœœœœ˜>Kšœ˜—Kšœœ˜Kšœ˜K˜—š Ÿœœœœœœ˜@Kšœ˜šœ ˜Kšœ œ˜Kšœ-˜-Kšœ.˜.Kšœœ˜—šœ ˜Kšœ œ˜Kšœ-˜-Kšœ.˜.Kšœœ˜—Kšœ˜ Kšœ˜K˜—šŸœœœœœœœ ˜šœœ ˜Kšœ ˜ Kšœ7˜7Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ ˜ Kšœ˜—Kšœ˜—K˜šœœ ˜)Kšœ œ˜Kšœœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—šœ œœ˜'K˜—šœœœœ˜˜>Kšœ˜K˜—šŸœœœœ5˜dKšœœ ˜%Kšœ5˜5Kšœ œœ˜3Kšœ˜K˜—šŸœœ˜#Kšœœ ˜%Kšœ˜Kšœ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ˜K˜—š Ÿ œœœ œœ˜4Kš œœœœœœ˜`Kšœ˜K˜—š Ÿœœœ œœœ ˜=Kš œœœœœœ˜CKšœ˜K˜—šŸ œœœœ˜KKšœœœ ˜Bšœ œ˜šœ œ˜%Kšœ˜Kšœ4˜4Kšœ˜—Kšœœ˜—šœœ ˜Kšœ˜Kšœw˜wKšœ˜KšœA˜AKšœ;˜;Kšœ9˜9Kšœ˜Kšœœ˜(Kšœ˜—Kšœ˜K˜—Kšœœœ˜@šœœ ˜*Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—šŸœœ œœ˜5Kšœ œœ ˜4Kšœ˜ Kšœ˜K˜—š Ÿ œœ œœœ ˜8Kšœ œœ ˜4Kšœ˜Kšœ œœ˜Kšœ œœ˜.š œœœœœ˜8Kšœ(˜(Kšœ˜—Kšœ ˜Kšœ˜K˜—šŸœœ(œ˜DKšœ œœ ˜4šœœ˜Kšœ%˜%Kšœ$˜$Kšœ&˜&Kšœ˜—Kšœ˜Kšœ˜K˜—šŸœœœœ5˜eKšœ œœ ˜4KšœN˜TKšœ˜K˜—šŸœœ˜$Kšœ œœ ˜4Kšœ˜Kšœœ˜Kšœ˜K˜—šŸœœœ(œ˜CKšœœœ*˜HKšœ˜K˜—š Ÿœœœœœ˜LKš œœœœœ˜[Kšœ˜K˜—šŸœœœ œ˜KKš œœœœœ˜fKšœ˜K˜—Kšœ œœ˜šŸœœœ˜#Kšœ œ œœœœœœ˜oKšœ˜K™—šŸ œœœ˜%Kšœœœœ˜'Kšœ˜K˜—š Ÿ œœœœœœ ˜MKšœœœœ˜!Kšœœœœ˜/šœ˜Kšœœœœ˜$Kšœ˜Kšœ˜—Kšœ œœœ˜2Kšœ˜Kšœ ˜Kšœ˜K˜—šŸ œœœœ ˜CKšœœœœ˜Kšœœœ!˜9šœ˜Kšœœœœ˜$Kšœ˜Kšœ˜K˜—Kšœ ˜Kšœ˜K™——K™Kšœ˜K˜Kšœ)™)Kšœ9™9Kšœ?™?Kšœ+™+K˜—…—‡ΔΏ‡