DIRECTORY Ascii USING [CR, SP, TAB], Atom USING [GetPName, GetPropFromList, MakeAtom, PropList], Convert USING [RopeFromInt], Imager USING [black, Box, Context, DoSave, DoSaveAll, MakeGray, MaskBox, MaskRectangle, MaskUnderline, Move, SetAmplifySpace, SetColor, SetFont, SetXRel, SetYRel, Show, StartUnderline, white], ImagerBackdoor USING [GetColor], ImagerColor USING [Color, ColorFromRGB, Find, RGB], ImagerColorFns USING [RGBFromHSV], ImagerFont USING [BoundingBox, Escapement, Extents, Font, FontBoundingBox, nullXChar, XChar], MessageWindow USING [Append], NodeProps USING [GetProp], NodeStyle USING [FontUnderlining, GetFirstIndent, GetLastLineFormatting, GetLeadingI, GetLeftIndent, GetLineFormatting, GetLineLength, GetReal, GetRestIndent, GetRightIndent, GetStrikeout, GetTabLoc, GetTabStops, GetUnderlining, GetVShift, PointsPerFil, RealParam, Ref, TabAlign, TabStop], NodeStyleFont USING [FontFromStyleParams], NodeStyleOps USING [ApplyLooks, ApplyObject, Copy, Create, GetStyleParam, GetStyleParamObj, nonNumeric, OfStyle], NodeStyleWorks USING [Where], Process USING [InitializeCondition, Milliseconds, MsecToTicks], Real USING [InlineRoundC, InlineRoundI, LargestNumber], RefTab USING [Create, Delete, Fetch, Ref, Store], Rope USING [ROPE, AppendChars], RuntimeError USING [UNCAUGHT], Scaled USING [Float, Floor, FromReal, GREATER, half, IntRep, LESS, MINUS, PLUS, Round, Scale, ValRep, Value, zero], TEditDocument USING [fatalTiogaError], TEditFormat USING [ArtworkClass, ArtworkClassRep, BoundingBoxProc, CharacterArtwork, CharacterArtworkClass, CharacterArtworkClassRep, CharacterArtworkRep, CharInfo, CharInfoEntry, CharInfoRec, CharNumber, CharPositionProc, FormatInfo, FormatInfoEntry, FormatInfoRec, FormatNumber, FormatProc, HyphenationPositions, HyphProc, LineInfo, LineInfoRec, maxHyph, PaintProc, PositionInfo, PositionInfoRec, ResolveProc], TextEdit, TextLooks USING [allLooks, Looks, noLooks], TextNode USING [Location, LocNumber, Ref], Vector2 USING [VEC], ViewerOps USING [BlinkDisplay]; TEditFormatImpl: CEDAR MONITOR IMPORTS Atom, Convert, Imager, ImagerBackdoor, ImagerColor, ImagerColorFns, ImagerFont, MessageWindow, NodeProps, NodeStyle, NodeStyleFont, NodeStyleOps, NodeStyleWorks, Process, Real, RefTab, Rope, RuntimeError, Scaled, TEditDocument, TextEdit, TextNode, ViewerOps EXPORTS TEditFormat ~ BEGIN XChar: TYPE ~ ImagerFont.XChar; Font: TYPE ~ ImagerFont.Font; ROPE: TYPE ~ Rope.ROPE; ArtworkClass: TYPE ~ TEditFormat.ArtworkClass; ArtworkClassRep: TYPE ~ TEditFormat.ArtworkClassRep; CharacterArtwork: TYPE ~ TEditFormat.CharacterArtwork; CharacterArtworkClass: TYPE ~ TEditFormat.CharacterArtworkClass; maxCharsPerLine: INT ~ MIN[LAST[TEditFormat.CharNumber], 8191]; minAmplifySpace: REAL _ 0.5; minHyphLetters: NAT _ 7; nullXChar: XChar ~ ImagerFont.nullXChar; debug: BOOL _ FALSE; FloorI: PROC [real: REAL] RETURNS [i: INTEGER] ~ { i _ Real.InlineRoundI[real]; IF i > real THEN i _ i - 1; }; CeilingI: PROC [real: REAL] RETURNS [i: INTEGER] ~ { i _ Real.InlineRoundI[real]; IF i < real THEN i _ i + 1; }; Ord: PROC [char: XChar] RETURNS [CARDINAL] ~ INLINE {RETURN [LOOPHOLE[char]]}; scratchLineInfo: TEditFormat.LineInfo _ NIL; scratchLineInfoCount: INT _ 0; scratchLineInfoLimit: INT _ 300; scratchLineInfoAllocated: INT _ 0; scratchLineInfoDestroyed: INT _ 0; Allocate: PUBLIC ENTRY PROC RETURNS [lineInfo: TEditFormat.LineInfo] ~ { IF scratchLineInfo # NIL THEN { lineInfo _ scratchLineInfo; scratchLineInfo _ lineInfo.link; lineInfo.link _ NIL; scratchLineInfoCount _ scratchLineInfoCount-1; } ELSE { lineInfo _ NEW[TEditFormat.LineInfoRec]; lineInfo.charInfo _ NEW[TEditFormat.CharInfoRec[125]]; lineInfo.formatInfo _ NEW[TEditFormat.FormatInfoRec[12]]; lineInfo.positionInfo _ NEW[TEditFormat.PositionInfoRec[126]]; scratchLineInfoAllocated _ scratchLineInfoAllocated + 1; }; }; Release: PUBLIC ENTRY PROC [lineInfo: TEditFormat.LineInfo] ~ { last: TEditFormat.LineInfo _ NIL; count: INT _ 0; FOR i: TEditFormat.LineInfo _ lineInfo, i.link UNTIL i=NIL DO f: TEditFormat.FormatInfo _ i.formatInfo; FOR i: NAT IN [0..f.length) DO f[i].font _ NIL; f[i].color _ NIL; f[i].tab _ NIL; ENDLOOP; i.artworkClass _ NIL; i.artworkData _ NIL; i.data _ NIL; i.index _ 0; f.length _ 0; last _ i; count _ count + 1; ENDLOOP; IF count > 0 THEN { last.link _ scratchLineInfo; scratchLineInfo _ lineInfo; scratchLineInfoCount _ scratchLineInfoCount + count; }; WHILE scratchLineInfoCount > scratchLineInfoLimit DO i: TEditFormat.LineInfo _ scratchLineInfo; scratchLineInfo _ i.link; scratchLineInfoCount _ scratchLineInfoCount - 1; i.charInfo _ NIL; i.formatInfo _ NIL; i.positionInfo _ NIL; scratchLineInfoDestroyed _ scratchLineInfoDestroyed + 1; ENDLOOP; }; widthCacheSize: NAT _ 12; widthCache: LIST OF WidthCacheRec _ NIL; widthCacheHits: INT _ 0; widthCacheMisses: INT _ 0; CommonCharCode: TYPE ~ [040B..176B]; CommonWidths: TYPE ~ REF CommonWidthsArray; CommonWidthsArray: TYPE ~ ARRAY CommonCharCode OF Scaled.Value; WidthCacheRec: TYPE ~ RECORD [ font: Font, widths: CommonWidths ]; CheckWidthCache: ENTRY PROC [font: Font] RETURNS [CommonWidths] ~ { prev: LIST OF WidthCacheRec _ NIL; FOR c: LIST OF WidthCacheRec _ widthCache, c.rest UNTIL c = NIL DO IF c.first.font = font THEN { IF prev # NIL THEN { prev.rest _ c.rest; c.rest _ widthCache; widthCache _ c; }; widthCacheHits _ widthCacheHits + 1; RETURN [c.first.widths]; }; prev _ c; ENDLOOP; widthCacheMisses _ widthCacheMisses + 1; RETURN [NIL] }; EnterWidthCache: ENTRY PROC [font: Font, widths: CommonWidths] ~ { new: LIST OF WidthCacheRec _ NIL; prev: LIST OF WidthCacheRec _ NIL; i: NAT _ 2; FOR p: LIST OF WidthCacheRec _ widthCache, p.rest DO IF p = NIL THEN {new _ LIST[[font, widths]]; EXIT}; IF i >= widthCacheSize AND p.rest#NIL THEN { new _ p.rest; p.rest _ NIL; new.rest _ NIL; new.first _ [font, widths]; EXIT; }; i _ i + 1; ENDLOOP; new.rest _ widthCache; widthCache _ new; }; ExpandChars: PROC [lineInfo: TEditFormat.LineInfo] ~ { oldCharInfo: TEditFormat.CharInfo ~ lineInfo.charInfo; oldPositionInfo: TEditFormat.PositionInfo ~ lineInfo.positionInfo; oldCharLimit: NAT ~ oldCharInfo.maxLength; charLimit: NAT ~ MIN[oldCharLimit + oldCharLimit/3 + 1, LAST[TEditFormat.CharNumber]-1]; newCharInfo: TEditFormat.CharInfo ~ NEW[TEditFormat.CharInfoRec[charLimit]]; newPositionInfo: TEditFormat.PositionInfo ~ NEW[TEditFormat.PositionInfoRec[charLimit+1]]; IF charLimit <= oldCharLimit THEN ERROR TEditDocument.fatalTiogaError; FOR i: NAT IN [0..oldCharLimit) DO newCharInfo[i] _ oldCharInfo[i]; newPositionInfo[i] _ oldPositionInfo[i]; ENDLOOP; newPositionInfo[oldCharLimit] _ oldPositionInfo[oldCharLimit]; lineInfo.charInfo _ newCharInfo; lineInfo.positionInfo _ newPositionInfo; }; ExpandFormats: PROC [lineInfo: TEditFormat.LineInfo] ~ { oldFormatInfo: TEditFormat.FormatInfo ~ lineInfo.formatInfo; oldFormatLimit: NAT ~ oldFormatInfo.maxLength; formatLimit: NAT ~ MIN[oldFormatLimit + oldFormatLimit/3 + 1, LAST[TEditFormat.FormatNumber]]; newFormatInfo: TEditFormat.FormatInfo ~ NEW[TEditFormat.FormatInfoRec[formatLimit]]; newFormatInfo.length _ oldFormatInfo.length; IF formatLimit <= oldFormatLimit THEN ERROR TEditDocument.fatalTiogaError; FOR i: NAT IN [0..oldFormatLimit) DO newFormatInfo[i] _ oldFormatInfo[i]; ENDLOOP; lineInfo.formatInfo _ newFormatInfo; }; ScratchRefs: TYPE ~ RECORD [ textBuffer: REF TEXT, charStyle: NodeStyle.Ref, tabStyle: NodeStyle.Ref ]; scratchRefsReleased: CONDITION; scratchRefsAvailable: BOOLEAN _ FALSE; scratchRefs: ScratchRefs; scratchRefsAllocCount: INT _ InitScratchRefs[333]; scratchRefsWaitCount: INT _ 0; -- stats only InitScratchRefs: ENTRY PROC [timeout: Process.Milliseconds] RETURNS [initialCount: INT _ 1] ~ TRUSTED { scratchRefs _ [NEW[TEXT[200]], NodeStyleOps.Create[], NodeStyleOps.Create[]]; scratchRefsAvailable _ TRUE; Process.InitializeCondition[@scratchRefsReleased, Process.MsecToTicks[timeout]]; }; AllocScratchRefs: ENTRY PROC RETURNS [allocated: ScratchRefs] ~ { IF NOT scratchRefsAvailable THEN { scratchRefsWaitCount _ scratchRefsWaitCount + 1; WAIT scratchRefsReleased }; IF scratchRefsAvailable THEN {scratchRefsAvailable _ FALSE; allocated _ scratchRefs; scratchRefs _ [NIL, NIL, NIL]} ELSE {allocated _ [NEW[TEXT[200]], NodeStyleOps.Create[], NodeStyleOps.Create[]]; scratchRefsAllocCount _ scratchRefsAllocCount + 1}; }; ReleaseScratchRefs: ENTRY PROC [allocated: ScratchRefs] ~ { IF NOT scratchRefsAvailable THEN { scratchRefs _ allocated; scratchRefsAvailable _ TRUE; NOTIFY scratchRefsReleased; }; }; GetFont: PUBLIC PROC [style: NodeStyle.Ref] RETURNS [font: Font _ NIL] ~ { font _ style.font; IF font = NIL THEN font _ NodeStyleFont.FontFromStyleParams[prefix: style.name[fontPrefix], family: style.name[fontFamily], face: style.fontFace, size: NodeStyle.GetReal[style, fontSize], alphabets: style.fontAlphabets]; }; GetStyledColor: PROC [style: NodeStyle.Ref, h: NodeStyle.RealParam _ textHue, s: NodeStyle.RealParam _ textSaturation, b: NodeStyle.RealParam _ textBrightness, name: ATOM _ $textNamedColor] RETURNS [color: ImagerColor.Color] ~ { color _ GetNamedColor[style, name]; IF color = NIL THEN color _ --GetHSBColor-- GetColor[style, h, s, b]; }; GetNamedColor: PROC [style: NodeStyle.Ref, name: ATOM] RETURNS [ImagerColor.Color] ~ { param: REF _ NIL; param _ NodeStyleOps.GetStyleParamObj[s: style, name: name, styleName: style.name[style], kind: IF style.print THEN print ELSE screen]; WITH param SELECT FROM a: ATOM => RETURN [IF a = $nil THEN NIL ELSE ImagerColor.Find[Atom.GetPName[a]]]; ENDCASE => RETURN [NIL]; }; GetColor: PUBLIC PROC [style: NodeStyle.Ref, h: NodeStyle.RealParam _ textHue, s: NodeStyle.RealParam _ textSaturation, b: NodeStyle.RealParam _ textBrightness] RETURNS [color: ImagerColor.Color] ~ { brightness: REAL ~ NodeStyle.GetReal[style, b]; IF brightness = 0.0 THEN { color _ Imager.black; } ELSE { saturation: REAL ~ NodeStyle.GetReal[style, s]; IF saturation = 0.0 THEN { color _ Imager.MakeGray[1.0-brightness]; } ELSE { hue: REAL ~ NodeStyle.GetReal[style, h]; color _ ImagerColor.ColorFromRGB[ImagerColorFns.RGBFromHSV[[H: hue, S: saturation, V: brightness]]]; }; }; }; GetWidthArray: PROC [font: Font] RETURNS [widths: CommonWidths] ~ { widths _ CheckWidthCache[font]; IF widths = NIL THEN { widths _ NEW[CommonWidthsArray]; FOR c: CommonCharCode IN CommonCharCode DO xchar: XChar ~ [set: 0, code: c]; widths[c] _ Scaled.FromReal[font.Escapement[xchar].x]; ENDLOOP; EnterWidthCache[font, widths]; }; }; TabState: TYPE ~ RECORD [ tabAlignmentChar: CHAR, tabAlignment: NodeStyle.TabAlign, doingTab: BOOL _ FALSE, tabStop: NodeStyle.TabStop, tabNumber: NAT _ 0, tabLoc, tabStart, tabWidth, tabTextStart: CARDINAL, prevTabLooks: TextLooks.Looks _ TextLooks.allLooks, tabCharLooks: TextLooks.Looks _ TextLooks.noLooks ]; oops: CARDINAL _ 0; -- should stay 0 unless there is a bug GetTabStop: PROC [style: NodeStyle.Ref, i: NAT] RETURNS [NodeStyle.TabStop] ~ { tabStop: NodeStyle.TabStop _ style.defaultTabStops; IF i < style.numTabStops THEN { t: LIST OF NodeStyle.TabStop _ style.tabStops; FOR j: NAT DECREASING IN (i..style.numTabStops) DO IF t = NIL THEN oops _ oops + 1 ELSE t _ t.rest; ENDLOOP; IF t = NIL THEN oops _ oops + 1 ELSE tabStop _ t.first; }; RETURN [tabStop] }; ComputeTabWidth: PROC [style: NodeStyle.Ref, spaceWidth: Scaled.Value, endX: Scaled.Value, nTabs: NAT] RETURNS [width: Scaled.Value] ~ { tabStop: NodeStyle.TabStop ~ GetTabStop[style, nTabs]; IF tabStop = NIL THEN { tabWidth: Scaled.Value _ Scaled.FromReal[MAX[NodeStyle.GetTabStops[style], 1.0]]; toNextTab: Scaled.Value _ tabWidth.MINUS[Scaled.ValRep[Scaled.IntRep[endX] MOD Scaled.IntRep[tabWidth]]]; width _ toNextTab; IF spaceWidth.GREATER[toNextTab] THEN width _ width.PLUS[tabWidth]; } ELSE { tabLoc: Scaled.Value ~ Scaled.FromReal[NodeStyle.GetTabLoc[tabStop, style]]; minLoc: Scaled.Value ~ endX.PLUS[spaceWidth]; newLoc: Scaled.Value ~ IF minLoc.GREATER[tabLoc] THEN minLoc ELSE tabLoc; width _ newLoc.MINUS[endX]; }; }; ComputePositionVector: PROC [lineInfo: TEditFormat.LineInfo] ~ { x: INTEGER _ lineInfo.positionInfo[0] _ 0; sx: Scaled.Value _ Scaled.half; IF lineInfo.nChars > 0 THEN TRUSTED { charEntry: LONG POINTER TO TEditFormat.CharInfoEntry _ @(lineInfo.charInfo[0]); FOR i: NAT IN [0..lineInfo.nChars) DO xNext: INTEGER _ Scaled.Floor[sx _ sx.PLUS[charEntry.width]]; IF xNext <= x THEN xNext _ x+1; x _ lineInfo.positionInfo[i+1] _ xNext; charEntry _ charEntry + SIZE[TEditFormat.CharInfoEntry]; ENDLOOP; }; }; FormatLine: PUBLIC TEditFormat.FormatProc ~ { lineInfo.startPos _ [node, startOffset]; lineInfo.ymax _ INTEGER.FIRST; lineInfo.ymin _ INTEGER.LAST; lineInfo.break _ eon; lineInfo.hasBackground _ FALSE; lineInfo.nChars _ 0; lineInfo.nBlankCharsAtEnd _ 0; lineInfo.startAmplifyIndex _ 0; lineInfo.amplifySpace _ 1.0; lineInfo.formatInfo.length _ 0; lineInfo.artworkClass _ NIL; lineInfo.artworkData _ NIL; IF node.hasartwork THEN { class: ArtworkClass ~ GetArtworkClassForNode[node]; IF class#NIL AND class.format#NIL THEN { lineInfo.artworkClass _ class; class.format[lineInfo, node, startOffset, nodeStyle, lineWidth, doLigsAndKern, kind]; RETURN; }; }; NormalFormatLine[lineInfo, node, startOffset, nodeStyle, lineWidth, doLigsAndKern, kind]; IF lineInfo.ymax < lineInfo.ymin THEN { lineInfo.ymax _ NodeStyle.GetLeadingI[nodeStyle]; lineInfo.ymin _ 0; }; }; xCR: XChar ~ [set: 0, code: ORD[Ascii.CR]]; xSP: XChar ~ [set: 0, code: ORD[Ascii.SP]]; xTAB: XChar ~ [set: 0, code: ORD[Ascii.TAB]]; xHyphen: XChar ~ [set: 41B, code: 76B]; xEnDash: XChar ~ [set: 357B, code: 44B]; xEmDash: XChar ~ [set: 357B, code: 45B]; xFigDash: XChar ~ [set: 357B, code: 46B]; xOldDash: XChar ~ [set: 0B, code: 30B]; xDiscHyphen: XChar ~ [set: 357B, code: 43B]; GetHyphenChar: PROC [style: NodeStyle.Ref, kind: NodeStyleOps.OfStyle] RETURNS [ImagerFont.XChar] ~ { param: REAL _ 45.0; param _ NodeStyleOps.GetStyleParam[s: style, name: $hyphenCode, styleName: style.name[style], kind: kind ! NodeStyleOps.nonNumeric => CONTINUE]; IF param < 0.0 OR param > 65278 THEN param _ 45.0; RETURN [LOOPHOLE[Real.InlineRoundC[param]]] }; GetMaxHorizontalExpansion: PROC [style: NodeStyle.Ref, kind: NodeStyleOps.OfStyle] RETURNS [REAL] ~ { param: REAL _ NodeStyle.PointsPerFil; param _ NodeStyleOps.GetStyleParam[s: style, name: $maxHorizontalExpansion, styleName: style.name[style], kind: kind ! NodeStyleOps.nonNumeric => CONTINUE]; RETURN [param] }; ExtraFormatInfo: TYPE ~ REF ExtraFormatInfoRep; ExtraFormatInfoRep: TYPE ~ RECORD [ charPropList: Atom.PropList, charPostfix: REF, characterArtwork: CharacterArtwork, underlineDY: REAL, underlineHeight: REAL, underlineColor: ImagerColor.Color, strikeoutDY: REAL, strikeoutHeight: REAL, strikeoutColor: ImagerColor.Color, letterspace: Scaled.Value, hShift: REAL, background: BOOL, backgroundAscent: REAL, backgroundDescent: REAL, backgroundColor: ImagerColor.Color, outlineboxBearoff: REAL, outlineboxThickness: REAL, outlineboxColor: ImagerColor.Color, hyphenChar: ImagerFont.XChar ]; GetPropFromList: PROC [list: Atom.PropList, key: ATOM] RETURNS [REF] ~ INLINE { RETURN [IF list = NIL THEN NIL ELSE Atom.GetPropFromList[list, key]] }; NormalFormatLine: PROC [ lineInfo: TEditFormat.LineInfo, node: TextNode.Ref, startOffset: INT, nodeStyle: NodeStyle.Ref, lineWidth: Scaled.Value, doLigsAndKern: BOOLEAN _ FALSE, kind: NodeStyleOps.OfStyle ] ~ { my: ScratchRefs _ AllocScratchRefs[]; realExtraIndent: REAL ~ ( IF startOffset = 0 THEN nodeStyle.GetFirstIndent[] ELSE nodeStyle.GetRestIndent[] ); realExtraIndentRight: REAL ~ ( IF startOffset = 0 THEN nodeStyle.GetReal[firstIndentRight] ELSE 0 ); extraIndent: Scaled.Value ~ Scaled.FromReal[realExtraIndent]; leftIndent: Scaled.Value ~ Scaled.FromReal[nodeStyle.GetLeftIndent + realExtraIndent]; rightIndent: Scaled.Value ~ Scaled.FromReal[nodeStyle.GetRightIndent + realExtraIndentRight]; lineLength: Scaled.Value ~ Scaled.FromReal[nodeStyle.GetLineLength]; trimmedLineWidth: Scaled.Value ~ (IF lineLength.GREATER[Scaled.zero] AND lineLength.LESS[lineWidth] THEN lineLength ELSE lineWidth).MINUS[leftIndent].MINUS[rightIndent]; fontWidths: CommonWidths _ NIL; text: REF TEXT ~ my.textBuffer; textStart, textEnd: INT _ 0; setRun: TextEdit.CharSetRun _ [0, 0, 0]; looksRun: TextEdit.LooksRun _ [TextLooks.noLooks, 0, 0]; propRun: TextEdit.PropListRun _ [NIL, 0, 0]; xchar: XChar _ [0, 0]; index: INT _ 0; curFormatNumber: TEditFormat.FormatNumber _ LAST[TEditFormat.FormatNumber]; prevLooks: TextLooks.Looks _ TextLooks.allLooks; looks: TextLooks.Looks _ TextLooks.noLooks; breakIndex: TEditFormat.CharNumber _ 0; endX: Scaled.Value _ Scaled.zero; breakX: Scaled.Value _ Scaled.zero; spaceAtEnd: Scaled.Value _ Scaled.zero; haveLetterspace: BOOL _ FALSE; -- set to true if any non-zero letterspace is found. prevLetterspace: Scaled.Value _ Scaled.zero; prevDoLetterspace: BOOL _ FALSE; breakSpaceAtEnd: Scaled.Value _ Scaled.zero; spaceWidths: Scaled.Value _ Scaled.zero; highWaterChars: NAT _ lineInfo.charInfo.maxLength; extension: ExtraFormatInfo _ NIL; characterPropertyList: Atom.PropList _ NIL; haveCharacterArtwork: BOOL _ FALSE; hyphenation: TEditFormat.HyphProc _ NIL; hyphenationData: REF _ NIL; NodeStyleExtents: PROC RETURNS [ymax, ymin: INTEGER] ~ { font: Font ~ GetFont[nodeStyle]; fontBoundingBox: ImagerFont.Extents _ ImagerFont.FontBoundingBox[font]; ymax _ CeilingI[fontBoundingBox.ascent]; ymin _ FloorI[-fontBoundingBox.descent]; }; mayAmplArt: BOOL _ FALSE; AmplArt: PROC [f: TEditFormat.FormatNumber] RETURNS [BOOL] ~ { ext: ExtraFormatInfo ~ NARROW[lineInfo.formatInfo[f].extension]; IF ext.characterArtwork # NIL THEN RETURN [ext.characterArtwork.amplified] ELSE RETURN [FALSE]; }; NewFormattingLooks: PROC ~ { realVShift: REAL; new: TEditFormat.FormatInfoEntry; newExtension: ExtraFormatInfo _ NIL; fontBoundingBox: ImagerFont.Extents; charStyle: NodeStyle.Ref _ nodeStyle; charPostfix: REF ~ GetPropFromList[characterPropertyList, $Postfix]; charArtwork: REF ~ GetPropFromList[characterPropertyList, $Artwork]; new.unique _ haveCharacterArtwork _ FALSE; IF charArtwork = NIL THEN { FOR f: TEditFormat.FormatNumber IN [0..MIN[9, lineInfo.formatInfo.length]) DO finfo: TEditFormat.FormatInfoEntry ~ lineInfo.formatInfo[f]; e: ExtraFormatInfo ~ NARROW[finfo.extension]; IF finfo.looks = looks AND charPostfix = e.charPostfix AND NOT finfo.unique THEN { curFormatNumber _ f; extension _ e; fontWidths _ GetWidthArray[finfo.font]; prevLooks _ looks; RETURN }; ENDLOOP; }; curFormatNumber _ lineInfo.formatInfo.length; IF curFormatNumber>=lineInfo.formatInfo.maxLength THEN ExpandFormats[lineInfo]; new.extension _ newExtension _ extension _ WITH lineInfo.formatInfo[curFormatNumber].extension SELECT FROM extra: ExtraFormatInfo => extra, ENDCASE => NEW[ExtraFormatInfoRep]; newExtension.charPropList _ characterPropertyList; newExtension.charPostfix _ charPostfix; newExtension.characterArtwork _ NIL; IF looks#TextLooks.noLooks THEN { charStyle _ my.charStyle; NodeStyleOps.Copy[dest: charStyle, source: nodeStyle]; NodeStyleOps.ApplyLooks[charStyle, looks, kind ! NodeStyleWorks.Where => { RESUME [Convert.RopeFromInt[from: TextNode.LocNumber[at: [node, index], skipCommentNodes: FALSE]]] }]; }; IF charPostfix # NIL THEN { IF charStyle = nodeStyle THEN { charStyle _ my.charStyle; NodeStyleOps.Copy[dest: charStyle, source: nodeStyle]; }; NodeStyleOps.ApplyObject[charStyle, charPostfix, kind]; }; WITH charArtwork SELECT FROM r: ROPE => { key: ATOM ~ Atom.MakeAtom[r]; class: CharacterArtworkClass ~ GetCharacterArtworkClass[key]; IF class # NIL THEN { newExtension.characterArtwork _ class.format[ class, [node, index], charStyle, kind ! RuntimeError.UNCAUGHT => IF NOT debug THEN { MessageWindow.Append[" TEditFormatImpl: Character Artwork Format bug", TRUE]; ViewerOps.BlinkDisplay[]; CONTINUE; } ]; IF newExtension.characterArtwork # NIL THEN { vShift: REAL ~ NodeStyle.GetVShift[charStyle]; new.unique _ TRUE; haveCharacterArtwork _ TRUE; IF newExtension.characterArtwork.amplified THEN mayAmplArt _ TRUE; lineInfo.ymax _ MAX[lineInfo.ymax, CeilingI[newExtension.characterArtwork.extents.ascent+vShift]]; lineInfo.ymin _ MIN[lineInfo.ymin, FloorI[vShift-newExtension.characterArtwork.extents.descent]]; }; }; }; ENDCASE => NULL; newExtension.letterspace _ Scaled.FromReal[charStyle.GetReal[letterspacing]]; IF newExtension.letterspace#Scaled.zero THEN haveLetterspace _ TRUE; new.font _ GetFont[charStyle]; new.color _ GetStyledColor[charStyle]; fontBoundingBox _ ImagerFont.FontBoundingBox[new.font]; new.looks _ looks; new.underlining _ NodeStyle.GetUnderlining[charStyle]; IF new.underlining # None THEN { underlineThickness: REAL ~ NodeStyle.GetReal[charStyle, underlineThickness]; underlineDescent: REAL ~ NodeStyle.GetReal[charStyle, underlineDescent]; newExtension.underlineHeight _ underlineThickness; newExtension.underlineDY _ underlineDescent; lineInfo.ymax _ MAX[lineInfo.ymax, CeilingI[-newExtension.underlineDY]]; lineInfo.ymin _ MIN[lineInfo.ymin, FloorI[-(newExtension.underlineHeight+newExtension.underlineDY)]]; newExtension.underlineColor _ GetStyledColor[charStyle, underlineHue, underlineSaturation, underlineBrightness, $underlineNamedColor]; }; new.strikeout _ NodeStyle.GetStrikeout[charStyle]; IF new.strikeout # None THEN { strikeoutThickness: REAL ~ NodeStyle.GetReal[charStyle, strikeoutThickness]; strikeoutAscent: REAL ~ NodeStyle.GetReal[charStyle, strikeoutAscent]; newExtension.strikeoutHeight _ strikeoutThickness; newExtension.strikeoutDY _ -strikeoutAscent; lineInfo.ymax _ MAX[lineInfo.ymax, CeilingI[-newExtension.strikeoutDY]]; lineInfo.ymin _ MIN[lineInfo.ymin, FloorI[-(newExtension.strikeoutHeight+newExtension.strikeoutDY)]]; newExtension.strikeoutColor _ GetStyledColor[charStyle, strikeoutHue, strikeoutSaturation, strikeoutBrightness, $strikeoutNamedColor]; }; -- Get background and outline box info -- { backgroundAscent: REAL ~ NodeStyle.GetReal[charStyle, backgroundAscent]; backgroundDescent: REAL ~ NodeStyle.GetReal[charStyle, backgroundDescent]; outlineboxThickness: REAL ~ MAX[NodeStyle.GetReal[charStyle, outlineboxThickness],0]; backgroundHeight: REAL ~ backgroundAscent+backgroundDescent; IF (newExtension.background _ (backgroundHeight > 0.0 OR outlineboxThickness > 0.0)) THEN { lineInfo.hasBackground _ TRUE; newExtension.backgroundAscent _ backgroundAscent; newExtension.backgroundDescent _ backgroundDescent; newExtension.backgroundColor _ GetStyledColor[charStyle, backgroundHue, backgroundSaturation, backgroundBrightness, $backgroundNamedColor]; newExtension.outlineboxBearoff _ NodeStyle.GetReal[charStyle, outlineboxBearoff]; IF backgroundHeight <= 0.0 THEN { newExtension.backgroundAscent _ fontBoundingBox.ascent + newExtension.outlineboxBearoff; newExtension.backgroundDescent _ fontBoundingBox.descent + newExtension.outlineboxBearoff; newExtension.backgroundColor _ Imager.white; }; newExtension.outlineboxThickness _ outlineboxThickness; newExtension.outlineboxColor _ GetStyledColor[charStyle, outlineboxHue, outlineboxSaturation, outlineboxBrightness, $outlineboxNamedColor]; lineInfo.ymax _ MAX[lineInfo.ymax, CeilingI[newExtension.backgroundAscent+outlineboxThickness]]; lineInfo.ymin _ MIN[lineInfo.ymin, FloorI[-(newExtension.backgroundDescent+outlineboxThickness)]]; }; }; newExtension.hShift _ NodeStyle.GetReal[charStyle, hshift]; realVShift _ NodeStyle.GetVShift[charStyle]; new.vShift _ realVShift; newExtension.hyphenChar _ GetHyphenChar[charStyle, kind]; lineInfo.ymax _ MAX[lineInfo.ymax, CeilingI[fontBoundingBox.ascent + realVShift]]; lineInfo.ymin _ MIN[lineInfo.ymin, FloorI[-fontBoundingBox.descent + realVShift]]; fontWidths _ GetWidthArray[new.font]; prevLooks _ looks; lineInfo.formatInfo[curFormatNumber] _ new; lineInfo.formatInfo.length _ curFormatNumber + 1; }; nodeSize: INT ~ TextEdit.Size[node]; nodeChars: INT ~ nodeSize-startOffset; maxNChars: TEditFormat.CharNumber ~ MIN[nodeChars, maxCharsPerLine]; nTabs: NAT _ 0; hyphenated: BOOL _ FALSE; lineInfo.nChars _ maxNChars; FOR curIndex: NAT IN [0..maxNChars) DO crBreak: BOOL _ FALSE; doLetterspace: BOOL _ TRUE; alteredWidth: BOOL _ FALSE; width: Scaled.Value _ Scaled.zero; TryBreakAfterDash: PROC ~ { IsLetter: PROC [xChar: XChar] RETURNS [BOOL] ~ INLINE { RETURN [Ord[xChar] IN [ORD['a]..ORD['z]] OR Ord[xChar] IN [ORD['A]..ORD['Z]]] }; IF curIndex > 0 AND IsLetter[lineInfo.charInfo[curIndex-1].char] AND NOT endX.PLUS[width].GREATER[trimmedLineWidth] AND curIndex < maxNChars-1 AND IsLetter[TextEdit.FetchChar[node, startOffset+(curIndex+1)]] THEN { breakIndex _ curIndex + 1; breakX _ endX.PLUS[width]; }; }; index _ startOffset+curIndex; IF index>=textEnd THEN { -- fetch more characters into buffer text.length _ 0; textStart _ index; textEnd _ textStart+Rope.AppendChars[buffer: text, rope: node.rope, start: textStart, len: maxNChars-curIndex]; }; xchar.code _ ORD[text[index-textStart]]; IF index>=setRun.end THEN { setRun _ TextEdit.FetchCharSetRun[node, index]; xchar.set _ setRun.charSet; }; IF index>=looksRun.end THEN { looksRun _ TextEdit.FetchLooksRun[node, index]; looks _ looksRun.looks; }; IF index>=propRun.end THEN { propRun _ TextEdit.FetchCharPropListRun[node, index]; characterPropertyList _ propRun.propList; prevLooks _ TextLooks.allLooks; -- to force NewFormattingLooks }; IF curIndex >= highWaterChars THEN { ExpandChars[lineInfo]; highWaterChars _ lineInfo.charInfo.maxLength; }; IF looks#prevLooks OR haveCharacterArtwork THEN NewFormattingLooks[]; IF haveCharacterArtwork THEN { width _ Scaled.FromReal[extension.characterArtwork.escapement.x]; doLetterspace _ FALSE; spaceAtEnd _ Scaled.zero; IF extension.characterArtwork.amplified THEN spaceWidths _ spaceWidths.PLUS[width]; } ELSE SELECT Ord[xchar] FROM Ord[xCR] => { crBreak _ TRUE; width _ fontWidths[Ord[xSP]]; breakIndex _ curIndex + 1; breakX _ endX.PLUS[width]; doLetterspace _ FALSE; }; Ord[xSP] => { width _ fontWidths[Ord[xSP]]; spaceWidths _ spaceWidths.PLUS[width]; breakIndex _ curIndex + 1; breakX _ endX.PLUS[width]; spaceAtEnd _ spaceAtEnd.PLUS[width]; doLetterspace _ FALSE; }; ORD['-] => { width _ fontWidths[Ord[xchar]]; spaceAtEnd _ Scaled.zero; TryBreakAfterDash[]; }; Ord[xTAB] => { spaceWidths _ Scaled.zero; lineInfo.startAmplifyIndex _ curIndex+1; width _ ComputeTabWidth[style: nodeStyle, spaceWidth: fontWidths[xSP.code], endX: endX, nTabs: nTabs]; breakIndex _ curIndex + 1; breakX _ endX.PLUS[width]; spaceAtEnd _ spaceAtEnd.PLUS[width]; nTabs _ nTabs + 1; doLetterspace _ FALSE; }; IN CommonCharCode => {width _ fontWidths[Ord[xchar]]; spaceAtEnd _ Scaled.zero}; ENDCASE => { font: Font ~ lineInfo.formatInfo[curFormatNumber].font; width _ Scaled.FromReal[ImagerFont.Escapement[font, xchar].x]; spaceAtEnd _ Scaled.zero; SELECT xchar FROM xHyphen, xEnDash, xEmDash, xFigDash, xOldDash => TryBreakAfterDash[]; xDiscHyphen => { xchar _ nullXChar; width _ Scaled.zero; alteredWidth _ TRUE; }; ENDCASE => NULL; }; IF haveLetterspace THEN { IF prevDoLetterspace AND doLetterspace AND curIndex > 0 THEN { pad: Scaled.Value ~ prevLetterspace.PLUS[extension.letterspace].Scale[-1]; lineInfo.charInfo[curIndex-1].alteredWidth _ pad#Scaled.zero; lineInfo.charInfo[curIndex-1].width _ lineInfo.charInfo[curIndex-1].width.PLUS[pad]; endX _ endX.PLUS[pad]; }; prevLetterspace _ extension.letterspace; }; prevDoLetterspace _ doLetterspace; lineInfo.charInfo[curIndex] _ [char: xchar, formatNumber: curFormatNumber, alteredWidth: alteredWidth, amplified: FALSE, width: width]; endX _ endX.PLUS[width]; IF crBreak THEN { lineInfo.nBlankCharsAtEnd _ 1; breakSpaceAtEnd _ width; lineInfo.break _ cr; lineInfo.nChars _ curIndex + 1; EXIT }; IF endX.MINUS[spaceAtEnd].GREATER[trimmedLineWidth] THEN { partialWordWidth: Scaled.Value ~ endX.MINUS[spaceAtEnd].MINUS[breakX]; lineInfo.break _ wrap; IF spaceAtEnd = Scaled.zero AND (lineInfo.nChars-breakIndex) >= minHyphLetters THEN { h: TEditFormat.HyphenationPositions ~ FindHyphenations[node, startOffset+breakIndex, nodeStyle, kind]; IF h[0] > 0 THEN { formatNum: NAT _ NAT.LAST; hyphChar: ImagerFont.XChar _ [0,0]; hyphWidth: Scaled.Value _ Scaled.zero; artChar: BOOL _ FALSE; GetHyphWidth: PROC [k: NAT] RETURNS [hyphW: Scaled.Value] ~ { f: NAT ~ lineInfo.charInfo[k].formatNumber; IF f # formatNum THEN { fie: TEditFormat.FormatInfoEntry ~ lineInfo.formatInfo[f]; extension: ExtraFormatInfo ~ NARROW[fie.extension]; hyphChar _ extension.hyphenChar; hyphWidth _ Scaled.FromReal[ImagerFont.Escapement[fie.font, hyphChar].x]; artChar _ extension.characterArtwork#NIL; formatNum _ f; }; RETURN [hyphWidth]; }; i: NAT _ 0; w: Scaled.Value _ breakX; b: NAT ~ breakIndex; j: NAT _ 0; WHILE b+j < curIndex AND i < TEditFormat.maxHyph AND h[i] > 0 AND trimmedLineWidth.GREATER[w.PLUS[GetHyphWidth[b+j]]] AND NOT artChar DO IF j = h[i] THEN { breakIndex _ b+j; breakX _ w.PLUS[hyphWidth]; hyphenated _ TRUE; i _ i + 1; }; w _ w.PLUS[lineInfo.charInfo[b+j].width]; j _ j + 1; ENDLOOP; IF hyphenated THEN { [] _ GetHyphWidth[breakIndex]; lineInfo.charInfo[breakIndex] _ [ char: hyphChar, formatNumber: formatNum, alteredWidth: FALSE, amplified: FALSE, width: hyphWidth ]; }; }; }; IF breakIndex>0 THEN {lineInfo.nChars _ breakIndex; endX _ breakX} ELSE IF curIndex>0 THEN {lineInfo.nChars _ curIndex; endX _ endX.MINUS[width]} ELSE {lineInfo.nChars _ 1}; EXIT; }; ENDLOOP; IF lineInfo.break = eon AND lineInfo.nChars < nodeChars THEN lineInfo.break _ wrap; IF lineInfo.ymax < lineInfo.ymin THEN [lineInfo.ymax, lineInfo.ymin] _ NodeStyleExtents[]; IF lineInfo.break = wrap THEN { breakSpaceAtEnd _ Scaled.zero; FOR j: NAT DECREASING IN [1..lineInfo.nChars) DO c: ImagerFont.XChar ~ lineInfo.charInfo[j].char; SELECT c FROM xSP => { width: Scaled.Value ~ lineInfo.charInfo[j].width; spaceWidths _ spaceWidths.MINUS[width]; breakSpaceAtEnd _ breakSpaceAtEnd.PLUS[width]; lineInfo.nBlankCharsAtEnd _ lineInfo.nBlankCharsAtEnd + 1; }; xTAB => { breakSpaceAtEnd _ breakSpaceAtEnd.PLUS[lineInfo.charInfo[j].width]; lineInfo.nBlankCharsAtEnd _ lineInfo.nBlankCharsAtEnd + 1; }; ENDCASE => EXIT; ENDLOOP; }; SELECT IF lineInfo.break = wrap THEN nodeStyle.GetLineFormatting[] ELSE nodeStyle.GetLastLineFormatting[] FROM FlushLeft => {lineInfo.xOffset _ leftIndent}; Justified => { lineInfo.xOffset _ leftIndent; IF spaceWidths.GREATER[Scaled.zero] THEN TRUSTED { residual: Scaled.Value ~ trimmedLineWidth.MINUS[endX.MINUS[breakSpaceAtEnd]]; maxHorizontalExpansion: REAL ~ GetMaxHorizontalExpansion[nodeStyle, kind]; amplify: REAL ~ MAX[MIN[Scaled.Float[residual.PLUS[spaceWidths]]/Scaled.Float[spaceWidths], maxHorizontalExpansion], minAmplifySpace]; start: NAT ~ lineInfo.startAmplifyIndex; end: NAT ~ lineInfo.nChars-lineInfo.nBlankCharsAtEnd; IF start < end THEN { entry: LONG POINTER TO TEditFormat.CharInfoEntry _ @(lineInfo.charInfo[start]); cachedWidth: Scaled.Value _ Scaled.zero; amplifiedWidth: Scaled.Value _ Scaled.zero; FOR i: NAT IN [start..end) DO IF entry.char=xSP OR (mayAmplArt AND AmplArt[entry.formatNumber]) THEN { IF entry.width # cachedWidth THEN { cachedWidth _ entry.width; amplifiedWidth _ Scaled.FromReal[Scaled.Float[cachedWidth]*amplify]; }; entry.width _ amplifiedWidth; entry.amplified _ TRUE; }; entry _ entry + SIZE[TEditFormat.CharInfoEntry]; ENDLOOP; }; lineInfo.amplifySpace _ amplify; }; }; FlushRight => {lineInfo.xOffset _ leftIndent.PLUS[trimmedLineWidth].MINUS[endX.MINUS[breakSpaceAtEnd]]}; Centered => { lineInfo.xOffset _ leftIndent.PLUS[trimmedLineWidth.MINUS[endX.MINUS[breakSpaceAtEnd]].Scale[-1]]; }; ENDCASE => ERROR; IF hyphenated THEN {lineInfo.nBlankCharsAtEnd_NAT.LAST}; IF lineInfo.amplifySpace = 1.0 THEN lineInfo.startAmplifyIndex _ lineInfo.nChars; lineInfo.nextPos _ lineInfo.startPos; lineInfo.nextPos.where _ lineInfo.nextPos.where + lineInfo.nChars; ComputePositionVector[lineInfo]; lineInfo.xmin _ 0; lineInfo.xmax _ lineInfo.positionInfo[lineInfo.nChars]; ReleaseScratchRefs[my]; }; HyphenationPositions: TYPE ~ TEditFormat.HyphenationPositions; HyphProc: TYPE ~ TEditFormat.HyphProc; FindHyphenations: PROC [node: TextNode.Ref, index: INT, style: NodeStyle.Ref, kind: NodeStyleOps.OfStyle] RETURNS [HyphenationPositions] ~ { hyphenationClass: ATOM ~ GetHyphenationClass[style, kind]; hyph: REF HyphRep ~ FetchHyph[hyphenationClass]; h: TEditFormat.HyphenationPositions _ ALL[0]; IF hyph # NIL THEN { hyphProc: TEditFormat.HyphProc _ hyph.hyphProc; hyphData: REF _ hyph.hyphData; size: INT ~ TextEdit.Size[node]; len: INT _ 0; WHILE index+len < size DO xchar: XChar ~ TextEdit.FetchChar[node, index+len]; set: NAT ~ xchar.set; char: CHAR ~ VAL[xchar.code]; alpha: BOOL ~ SELECT set FROM 0 => char IN ['a..'z] OR char IN ['A..'Z] OR char IN ['\301..'\321]--accents--, 46B--greek--, 47B--cyrillic-- => TRUE, 357B => (char=43C), --discretionary hyphen-- ENDCASE => FALSE; IF NOT alpha THEN EXIT; len _ len + 1; ENDLOOP; IF hyphProc#NIL AND len > 0 THEN h _ hyphProc[node: node, start: index, len: len, hyphData: hyphData]; }; RETURN [h] }; HyphRep: TYPE ~ RECORD [hyphProc: HyphProc, hyphData: REF]; hyphTab: RefTab.Ref ~ RefTab.Create[mod: 3]; GetHyphenationClass: PROC [style: NodeStyle.Ref, kind: NodeStyleOps.OfStyle] RETURNS [ATOM] ~ { param: REF _ NIL; param _ NodeStyleOps.GetStyleParamObj[s: style, name: $hyphenation, styleName: style.name[style], kind: kind]; WITH param SELECT FROM atom: ATOM => RETURN [atom]; ENDCASE => RETURN [NIL]; }; FetchHyph: ENTRY PROC [hyphenationClass: ATOM] RETURNS [REF HyphRep] ~ { ENABLE UNWIND => NULL; RETURN [NARROW[RefTab.Fetch[hyphTab, hyphenationClass].val]] }; RegisterHyphenation: PUBLIC ENTRY PROC [hyphenationClass: ATOM, hyphProc: HyphProc, hyphData: REF] RETURNS [oldProc: HyphProc_NIL, oldData: REF_NIL] ~ { ENABLE UNWIND => NULL; old: REF HyphRep ~ NARROW[RefTab.Fetch[hyphTab, hyphenationClass].val]; [] _ RefTab.Store[hyphTab, hyphenationClass, IF hyphProc = NIL THEN NIL ELSE NEW[HyphRep _ [hyphProc, hyphData]]]; IF old # NIL THEN {oldProc _ old.hyphProc; oldData _ old.hyphData}; }; CharClass: PROC [char: XChar] RETURNS [charClass: NodeStyle.FontUnderlining] ~ { IF char.set = 0 THEN { charClass _ SELECT CHAR[VAL[char.code]] FROM Ascii.SP, Ascii.TAB, Ascii.CR => All, IN ['a..'z], IN ['A..'Z], IN ['0..'9] => LettersAndDigits, ENDCASE => Visible; } ELSE IF char.code MOD 128 IN [041B..177B) THEN charClass _ Visible ELSE charClass _ All; }; Paint: PUBLIC TEditFormat.PaintProc ~ { class: ArtworkClass ~ lineInfo.artworkClass; IF class # NIL AND class.paint#NIL THEN class.paint[lineInfo, context] ELSE NormalPaint[lineInfo, context]; }; PaintBackground: PROC [lineInfo: TEditFormat.LineInfo, context: Imager.Context] ~ { nChars: NAT ~ lineInfo.nChars+(IF lineInfo.nBlankCharsAtEnd=NAT.LAST THEN 1 ELSE -lineInfo.nBlankCharsAtEnd); charInfo: TEditFormat.CharInfo ~ lineInfo.charInfo; formatInfo: TEditFormat.FormatInfo ~ lineInfo.formatInfo; lastSetColor: ImagerColor.Color _ Imager.black; -- assume black coming in SetColor: PROC [color: ImagerColor.Color] ~ INLINE {IF lastSetColor # color THEN {Imager.SetColor[context, color]; lastSetColor _ color}}; Action: PROC ~ { Boxes: PROC [outline: BOOL] ~ { prevFormatNumber: NAT _ NAT.LAST; x: Scaled.Value _ Scaled.zero; pad: ImagerFont.Extents _ [0,0,0,0]; xMin: REAL _ 0.0; EmitBox: PROC ~ { xMax: REAL ~ Scaled.Float[x]; IF pad.ascent + pad.descent > 0 THEN Imager.MaskBox[context, [xmin: xMin - pad.leftExtent, ymin: -pad.descent, xmax: xMax + pad.rightExtent, ymax: pad.ascent]]; xMin _ xMax; }; FOR i: NAT IN [0..nChars) DO formatNumber: TEditFormat.FormatNumber ~ charInfo[i].formatNumber; format: TEditFormat.FormatInfoEntry ~ formatInfo[formatNumber]; extension: ExtraFormatInfo ~ NARROW[format.extension]; thickness: REAL ~ IF outline THEN extension.outlineboxThickness ELSE 0; something: BOOL ~ (NOT outline) OR thickness > 0.0; IF prevFormatNumber # formatNumber THEN { EmitBox[]; pad _ IF extension.background AND something THEN [ leftExtent: extension.outlineboxBearoff + thickness, rightExtent: extension.outlineboxBearoff + thickness, ascent: extension.backgroundAscent + thickness, descent: extension.backgroundDescent + thickness ] ELSE [0,0,0,0]; IF extension.background THEN SetColor[IF outline THEN extension.outlineboxColor ELSE extension.backgroundColor]; prevFormatNumber _ formatNumber; }; x _ x.PLUS[charInfo[i].width]; ENDLOOP; EmitBox[]; }; Imager.Move[context]; Boxes[outline: TRUE]; Boxes[outline: FALSE]; }; Imager.DoSave[context, Action]; }; colorBug: CARDINAL _ 0; NormalPaint: PUBLIC PROC [lineInfo: TEditFormat.LineInfo, context: Imager.Context] ~ { nChars: NAT ~ lineInfo.nChars+(IF lineInfo.nBlankCharsAtEnd=NAT.LAST THEN 1 ELSE -lineInfo.nBlankCharsAtEnd); i: NAT _ 0; charInfo: TEditFormat.CharInfo ~ lineInfo.charInfo; formatInfo: TEditFormat.FormatInfo ~ lineInfo.formatInfo; lastSetColor: ImagerColor.Color _ Imager.black; -- assume black coming in SetColor: PROC [color: ImagerColor.Color] ~ INLINE {IF lastSetColor # color THEN {Imager.SetColor[context, color]; lastSetColor _ color}}; lastAmplify: REAL _ 1.0; -- assume no amplification coming in SetAmplifySpace: PROC [a: REAL] ~ INLINE {IF a # lastAmplify THEN {Imager.SetAmplifySpace[context, a]; lastAmplify _ a}}; lastSetFont: Font _ NIL; IF context.state#NIL AND ImagerBackdoor.GetColor[context]#Imager.black THEN colorBug _ colorBug + 1; IF lineInfo.hasBackground THEN PaintBackground[lineInfo, context]; WHILE i < nChars DO formatNumber: TEditFormat.FormatNumber ~ charInfo[i].formatNumber; format: TEditFormat.FormatInfoEntry ~ formatInfo[formatNumber]; extension: ExtraFormatInfo ~ NARROW[format.extension]; worry: BOOL ~ NOT (format.underlining = None AND format.strikeout = None); doUnderline: BOOLEAN _ FALSE; doStrikeout: BOOLEAN _ FALSE; deltaWidth: REAL _ 0.0; tabFound: BOOL _ FALSE; newi: NAT _ 0; ShowProc: PROC [charAction: PROC [char: XChar]] ~ TRUSTED { info: TEditFormat.CharInfo ~ charInfo; end: NAT ~ nChars; f: TEditFormat.FormatNumber ~ formatNumber; whatMeWorry: BOOL ~ worry; j: NAT _ i; entry: LONG POINTER TO TEditFormat.CharInfoEntry _ IF j < end THEN @(info[j]) ELSE NIL; WHILE j < end DO char: XChar ~ entry.char; IF entry.formatNumber # f THEN EXIT; IF whatMeWorry THEN { charClass: NodeStyle.FontUnderlining ~ CharClass[char]; charUnderline: BOOLEAN ~ format.underlining >= charClass; charStrikeout: BOOLEAN ~ format.strikeout >= charClass; IF doUnderline # charUnderline OR doStrikeout # charStrikeout THEN EXIT; }; IF char=xTAB THEN {tabFound _ TRUE; EXIT}; IF NOT (char=nullXChar AND entry.alteredWidth) THEN charAction[char]; j _ j + 1; IF j = lineInfo.startAmplifyIndex THEN EXIT; IF entry.alteredWidth THEN { deltaWidth _ Scaled.Float[entry.width] - ImagerFont.Escapement[lineInfo.formatInfo[entry.formatNumber].font, char].x; EXIT; }; entry _ entry + SIZE[TEditFormat.CharInfoEntry]; ENDLOOP; newi _ j; }; IF lastSetFont # format.font THEN { Imager.SetFont[context, format.font]; lastSetFont _ format.font; }; IF i = lineInfo.startAmplifyIndex THEN SetAmplifySpace[lineInfo.amplifySpace]; SetColor[format.color]; IF worry THEN { startCharClass: NodeStyle.FontUnderlining ~ CharClass[charInfo[i].char]; doUnderline _ format.underlining >= startCharClass; doStrikeout _ format.strikeout >= startCharClass; }; IF doUnderline OR doStrikeout THEN { Imager.StartUnderline[context]; }; IF format.vShift # 0.0 THEN { Imager.SetYRel[context, format.vShift]; }; IF extension.hShift # 0.0 THEN { Imager.SetXRel[context, extension.hShift]; }; IF extension.characterArtwork = NIL THEN { Imager.Show[context, ShowProc]; i _ newi} ELSE { do: PROC ~ {extension.characterArtwork.paint[extension.characterArtwork, context]}; x: REAL ~ extension.characterArtwork.escapement.x; Imager.DoSaveAll[context, do ! RuntimeError.UNCAUGHT => IF NOT debug THEN { MessageWindow.Append[" TEditFormatImpl: Character Artwork Paint bug", TRUE]; ViewerOps.BlinkDisplay[]; } ]; Imager.SetXRel[context, Scaled.Float[charInfo[i].width]]; i _ i + 1; }; IF tabFound THEN { entry: TEditFormat.CharInfoEntry ~ charInfo[i]; Imager.SetXRel[context, Scaled.Float[entry.width]]; i _ i + 1; }; IF format.vShift # 0.0 THEN { Imager.SetYRel[context, -format.vShift]; }; IF extension.hShift # 0.0 OR deltaWidth#0.0 THEN { Imager.SetXRel[context, deltaWidth-extension.hShift]; deltaWidth _ 0; }; IF doUnderline THEN { SetColor[extension.underlineColor]; Imager.MaskUnderline[context, extension.underlineDY, extension.underlineHeight]; SetColor[format.color]; }; IF doStrikeout THEN { SetColor[extension.strikeoutColor]; Imager.MaskUnderline[context, extension.strikeoutDY, extension.strikeoutHeight]; SetColor[format.color]; }; ENDLOOP; SetColor[Imager.black]; SetAmplifySpace[1.0]; }; Resolve: PUBLIC TEditFormat.ResolveProc ~ { class: ArtworkClass ~ lineInfo.artworkClass; [loc, xmin, width, rightOfLine] _ IF class # NIL AND class.resolve#NIL THEN class.resolve[lineInfo, x] ELSE NormalResolve[lineInfo, x]; }; NormalResolve: PROC [lineInfo: TEditFormat.LineInfo, x: INTEGER] RETURNS [loc: TextNode.Location, xmin, width: INTEGER, rightOfLine: BOOLEAN] ~ { xOffset: INT _ lineInfo.xOffset.Round[]; xOfi: INT _ lineInfo.positionInfo[0]; xx: INT _ INT[x] - xOffset; FOR i: NAT IN [0..lineInfo.nChars) DO xOfiPlusOne: INT ~ lineInfo.positionInfo[i+1]; IF i = lineInfo.nChars - 1 OR xxlineInfo.nChars THEN {x _ LAST[INTEGER]; width _ 0} ELSE IF i=lineInfo.nChars THEN {x _ lineInfo.positionInfo[i] + lineInfo.xOffset.Round[]; width _ 0} ELSE { x _ lineInfo.positionInfo[i]; width _ lineInfo.positionInfo[i+1] - x; x _ x + lineInfo.xOffset.Round[]; }; }; }; BoundingBox: PUBLIC TEditFormat.BoundingBoxProc ~ { class: ArtworkClass ~ lineInfo.artworkClass; RETURN [ IF class # NIL AND class.boundingBox#NIL THEN class.boundingBox[lineInfo, start, length] ELSE NormalBoundingBox[lineInfo, start, length] ]; }; NormalBoundingBox: PROC [lineInfo: TEditFormat.LineInfo, start, length: INT] RETURNS [Imager.Box] ~ { charInfo: TEditFormat.CharInfo ~ lineInfo.charInfo; formatInfo: TEditFormat.FormatInfo ~ lineInfo.formatInfo; x0: REAL _ Real.LargestNumber; y0: REAL _ Real.LargestNumber; x1: REAL _ -Real.LargestNumber; y1: REAL _ -Real.LargestNumber; x: REAL _ 0.0; BBPoint: PROC [p: Vector2.VEC] ~ { IF p.x < x0 THEN x0 _ p.x; IF p.x > x1 THEN x1 _ p.x; IF p.y < y0 THEN y0 _ p.y; IF p.y > y1 THEN y1 _ p.y; }; BBox: PROC [p0, p1: Vector2.VEC] ~ { BBPoint[p0]; BBPoint[p1]; }; FOR i: INT IN [0..MIN[start+length, lineInfo.nChars]) DO c: TEditFormat.CharInfoEntry ~ charInfo[i]; f: TEditFormat.FormatInfoEntry ~ formatInfo[c.formatNumber]; ext: ExtraFormatInfo ~ NARROW[f.extension]; w: REAL ~ Scaled.Float[c.width]; newx: REAL ~ x+w; IF i >= start THEN { charClass: NodeStyle.FontUnderlining ~ CharClass[charInfo[i].char]; doUnderline: BOOL ~ f.underlining >= charClass; doStrikeout: BOOL ~ f.strikeout >= charClass; y: REAL ~ f.vShift; extent: ImagerFont.Extents ~ IF ext.characterArtwork # NIL THEN ext.characterArtwork.extents ELSE ImagerFont.BoundingBox[f.font, c.char]; BBox[[x-extent.leftExtent, y-extent.descent], [x+extent.rightExtent, y+extent.ascent]]; IF doUnderline THEN { BBox[[x, -ext.underlineDY], [newx, -ext.underlineDY-ext.underlineHeight]]; }; IF doStrikeout THEN { BBox[[x, -ext.strikeoutDY], [newx, -ext.strikeoutDY-ext.strikeoutHeight]]; }; }; x _ newx; ENDLOOP; IF x0 >= x1 THEN RETURN [[0, 0, 0, 0]] ELSE RETURN [[xmin: x0, ymin: y0, xmax: x1, ymax: y1]]; }; RegisterCharacterArtwork: PUBLIC PROC [class: CharacterArtworkClass] ~ { [] _ RefTab.Store[characterArtworkRegistry, class.name, class]; }; UnregisterCharacterArtwork: PUBLIC PROC [name: ATOM] ~ { [] _ RefTab.Delete[characterArtworkRegistry, name]; }; GetCharacterArtworkClass: PUBLIC PROC [name: ATOM] RETURNS [CharacterArtworkClass] ~ { IF NOT artworkEnabled THEN RETURN [NIL]; RETURN [NARROW[RefTab.Fetch[characterArtworkRegistry, name].val]]; }; characterArtworkRegistry: RefTab.Ref ~ RefTab.Create[mod: 5]; VRuleDataRep: TYPE ~ RECORD [ color: ImagerColor.Color, ascent: REAL, descent: REAL, width: REAL, bearoff: REAL ]; VRulePaint: PROC [self: CharacterArtwork, context: Imager.Context] ~ { data: REF VRuleDataRep ~ NARROW[self.data]; Imager.Move[context]; Imager.SetColor[context, data.color]; Imager.MaskRectangle[context, [x: data.bearoff, y: -data.descent, w: data.width, h: data.ascent+data.descent]]; }; VRuleFormat: PROC [class: CharacterArtworkClass, loc: TextNode.Location, style: NodeStyle.Ref, kind: NodeStyleOps.OfStyle] RETURNS [CharacterArtwork] ~ { letter: XChar ~ TextEdit.FetchChar[loc.node, loc.where]; IF letter=[0, ORD['|]] THEN { ascent: REAL _ NodeStyle.GetReal[style, backgroundAscent]; descent: REAL _ NodeStyle.GetReal[style, backgroundDescent]; width: REAL ~ NodeStyle.GetReal[style, outlineboxThickness]; bearoff: REAL ~ NodeStyle.GetReal[style, outlineboxBearoff]; IF ascent+descent <= 0.0 THEN { fontBoundingBox: ImagerFont.Extents ~ ImagerFont.FontBoundingBox[GetFont[style]]; ascent _ fontBoundingBox.ascent + bearoff; descent _ fontBoundingBox.descent - bearoff; }; IF width > 0.0 THEN { data: REF VRuleDataRep ~ NEW[VRuleDataRep _ [ color: GetStyledColor[style, outlineboxHue, outlineboxSaturation, outlineboxBrightness, $outlineBoxNamedColor], ascent: ascent, descent: descent, width: width, bearoff: bearoff ]]; extents: ImagerFont.Extents ~ [leftExtent: -data.bearoff, rightExtent: data.bearoff+data.width, ascent: data.ascent, descent: data.descent]; RETURN [NEW[TEditFormat.CharacterArtworkRep _ [paint: VRulePaint, extents: extents, amplified: TRUE, escapement: [data.width+2*data.bearoff, 0], data: data]]] }; }; RETURN [NIL]; -- treat like normal character }; vRuleClass: CharacterArtworkClass ~ NEW[TEditFormat.CharacterArtworkClassRep _ [ name: $VRule, format: VRuleFormat, data: NIL ]]; RegisterArtwork: PUBLIC PROC [a: ArtworkClass] ~ { [] _ RefTab.Store[artworkRegistry, a.name, a]; }; UnRegisterArtwork: PUBLIC PROC [a: ATOM] ~ { [] _ RefTab.Delete[artworkRegistry, a]; }; GetArtworkClass: PUBLIC PROC [a: ATOM] RETURNS [ArtworkClass] ~ { RETURN [NARROW[RefTab.Fetch[artworkRegistry, a].val]]; }; GetArtworkClassForNode: PROC [node: TextNode.Ref] RETURNS [ArtworkClass] ~ { className: ATOM _ NIL; prop: REF ~ NodeProps.GetProp[node, $Artwork]; WITH prop SELECT FROM a: ATOM => className _ a; r: ROPE => className _ Atom.MakeAtom[r]; ENDCASE => NULL; IF className#NIL THEN WITH RefTab.Fetch[artworkRegistry, className].val SELECT FROM a: ArtworkClass => RETURN [IF artworkEnabled THEN a ELSE NIL]; ENDCASE => NULL; RETURN [NIL]; }; artworkRegistry: RefTab.Ref ~ RefTab.Create[mod: 5]; nonArtworkClass: ArtworkClass ~ NEW[ArtworkClassRep _ [ name: NIL, format: NormalFormatLine, paint: NormalPaint, resolve: NormalResolve, charPosition: NormalCharPosition, boundingBox: NormalBoundingBox ]]; artworkEnabled: BOOL _ FALSE; ArtworkEnabled: PUBLIC ENTRY PROC RETURNS [BOOL] ~ {RETURN [artworkEnabled]}; SetArtworkEnabled: PUBLIC ENTRY PROC [enabled: BOOL] RETURNS [was: BOOL] ~ {was _ artworkEnabled; artworkEnabled _ enabled}; Silly: HyphProc ~ { h: HyphenationPositions _ [3,6,9,12,15,18,21,0,0,0,0,0,0,0,0,0]; RETURN [h]; }; RegisterArtwork[nonArtworkClass]; RegisterCharacterArtwork[vRuleClass]; [] _ RegisterHyphenation[$Silly, Silly, NIL]; END. ’TEditFormatImpl.mesa Copyright Σ 1983, 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. Michael Plass, November 25, 1987 11:15:16 am PST Rick Beach, July 15, 1987 11:10:44 pm PDT Doug Wyatt, February 17, 1988 4:22:31 pm PST The 8191 comes from the stupid limit on nChars in TEditDocument.LineRec Basic functions Avoid having unneeded REFs hanging around. f[i].charProps is retained for re-use. Cache of character width arrays Move to front NB: The following code forces an effective cache size of >= 2 Formatting Expands the charInfo and positionInfo sequences by about 34% Expands the formatInfo sequence by about 34% This scratch storage will will normally be allocated only once, but in exceptional circumstances a new batch may be needed; Uses the cached refs if they are available or become available after a short wait; otherwise allocates a new batch. this should become the public proc in future; RJB July 15, 1987 rename this procedure to GetHSBColor in future; RJB July 15, 1987 Note that duplicate entries might be cached, but eventually one will get flushed. NOTE - The charProps REF ANY of the FormatInfoEntry record type is usurped to be used as a catch-all extension mechanism. Instead of keeping track of the character property list, it keeps track of this plus extra relevant information that we forgot to put in the interface. The field should be renamed to "extension" when we get a chance. - MFP Formatting constants for the line: Basic formatting variables: tabs: TabState; First a quick look through the first few formats to see if it is a common one. NodeStyleOps.Copy[dest: my.tabStyle, source: nodeStyle]; -- may need for fancy tabs. Uses curIndex, lineInfo, endX, width, trimmedLineWidth; modifies breakIndex, breakX N.B. sets above parameters Painting Character positions Character Artwork classes Character Artwork example implementation This is a sample implementation of a character artwork class. By placing a character property with key Artwork and value VRule on a vertical bar character, it can be made to format as a vertical rule with a thickness and height matching that of the outline box. vRuleClass is registered below Artwork classes Κ5y˜codešœ™KšœT™TKšœ0™0K™)Kšœ,™,—K™šΟk ˜ Kš œœœœœ˜Kšœœ1˜;Kšœœ˜Kšœœ΄˜ΐKšœœ ˜ Kšœ œœ˜3Kšœœ˜"Kšœ œM˜]Kšœœ ˜Kšœ œ ˜Kšœ œ’˜‘Kšœœ˜*Kšœ œ_˜qKšœœ ˜Kšœœ2˜?Kšœœ-˜7Kšœœ%˜1Kšœœœ˜Kšœ œœ˜Kš œœœœœœ%˜sKšœœ˜&Kšœ œ‹˜œKšœ ˜ Kšœ œ˜+Kšœ œ˜*Kšœœœ˜Kšœ œ˜—K˜KšΠblœœ˜Kšœ‚˜‰Kšœ ˜šœ˜K˜Kšœœ˜Kšœœ˜šœœœ˜K˜—Kšœœ˜.šœœ˜4K˜—Kšœœ ˜6Kšœœ%˜@K˜šœœœœ ˜?KšœG™GK™—šœœ˜K˜—šœœ˜K™—šœ(˜(K™—Kšœœœ˜—head™š Οnœœœœœ˜2K˜Kšœ œ ˜K˜K˜—š Ÿœœœœœ˜4K˜Kšœ œ ˜K˜K˜—šŸœœœœœœœ ˜NK˜—Kšœ(œ˜,Kšœœ˜Kšœœ˜ Kšœœ˜"šœœ˜"K˜—š Ÿœœœœœ%˜Hšœœœ˜Kšœ˜Kšœ ˜ Kšœœ˜Kšœ.˜.Kšœ˜—šœ˜Kšœ œ˜(Kšœœ˜6Kšœœ ˜9Kšœœ#˜>Kšœ8˜8K˜—K˜K˜—šŸœœœœ%˜?Kšœœ˜!Kšœœ˜šœ,œœ˜=Kšœ)˜)šœœœ˜Kšœ*™*Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ&™&Kšœ˜—Kšœœ˜Kšœœ˜Kšœ œ˜ Kšœ ˜ K˜ Kšœ ˜ Kšœ˜Kšœ˜—šœ œ˜Kšœ˜Kšœ˜Kšœ4˜4Kšœ˜—šœ-˜4Kšœ*˜*Kšœ˜Kšœ0˜0Kšœ œ˜Kšœœ˜Kšœœ˜Kšœ8˜8Kšœ˜—K˜K˜——™Kšœœ˜Kšœ œœœ˜(Kšœœ˜šœœ˜K˜—Kšœœ˜$Kšœœœ˜+šœœœœ˜?K˜—šœœœ˜K˜ Kšœ˜Kšœ˜K˜—šŸœœœœ˜CKšœœœœ˜"š œœœ$œœ˜Bšœœ˜šœœœ˜K™ Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ$˜$Kšœ˜Kšœ˜—Kšœ ˜ Kšœ˜—Kšœ(˜(Kšœœ˜ Kšœ˜K˜—šŸœœœ'˜BKšœœœœ˜!Kšœœœœ˜"Kšœœ˜ Kšœ=™=šœœœ$˜4Kš œœœœœ˜3šœœœœ˜,Kšœ ˜ Kšœ œ˜ Kšœ œ˜Kšœ˜Kšœ˜Kšœ˜—K˜ Kšœ˜—Kšœ˜Kšœ˜Kšœ˜K˜——™ šŸ œœ%˜6Kšœ<™˜>K˜ K˜(K˜K˜—šŸ œœ%˜8Kšœ,™,Kšœ<˜Kšœœ#˜@Kšœœœœ!˜JKšœœœ˜Kšœ˜—šŸœœ˜Kšœ œ˜K˜!Kšœ œ˜$K˜$K˜%Kšœ œ4˜DKšœ œ4˜DKšœN™NKšœ$œ˜*šœœœ˜šœœœ!˜MKšœ<˜K˜—šœœ˜$Kšœ˜Kšœ-˜-Kšœ˜—Kšœœœ˜Ešœœ˜KšœA˜AKšœœ˜Kšœ˜Kšœ&œœ˜SKšœ˜—šœœ ˜šœ ˜ Kšœ œ˜Kšœ˜Kšœ˜Kšœœ˜Kšœœ˜Kšœ˜—šœ ˜ Kšœ˜Kšœœ˜&Kšœ˜Kšœœ˜Kšœœ˜$Kšœœ˜Kšœ˜—šœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœ˜Kšœ˜Kšœ(˜(Kšœf˜fKšœ˜Kšœœ˜Kšœœ˜$Kšœ˜Kšœœ˜Kšœ˜—KšœN˜Pšœ˜ Kšœ7˜7Kšœ>˜>Kšœ˜šœ˜KšœE˜Ešœ˜Kšœ˜Kšœ˜Kšœœ˜Kšœ˜—Kšœœ˜—Kšœ˜——šœœ˜šœœœœ˜>Kšœ$œ"˜JKšœ=˜=KšœJœ˜TKšœ œ˜K˜—Jšœ(˜(Kšœ˜—Jšœ"˜"Kšœsœ˜ˆKšœ œ˜šœ œ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kš˜Kšœ˜—šœœ œœ˜:Kšœ&œ œ ˜FK˜šœœ0œ˜UKšœf˜fšœ œ˜Kšœ œœœ˜Kšœ#˜#Kšœ&˜&Kšœ œœ˜šŸ œœœœ˜=KšΟbœ™Kšœœ%˜+šœœ˜Kšœ:˜:Kšœœ˜3Kšœ ˜ KšœI˜IKšœ%œ˜)Kšœ˜Kšœ˜—Kšœ ˜Kšœ˜—Kšœœ˜ Kšœ˜Kšœœ˜Kšœœ˜ šœœœ œœœœœ ˜ˆšœ œ˜Kšœ˜Kšœ œ ˜Kšœ œ˜K˜ Kšœ˜—Kšœœ˜)K˜ Kšœ˜—šœ œ˜Kšœ˜šœ!˜!Kšœ˜Kšœ˜Kšœœ˜Kšœ œ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšœ˜—Kšœœ.˜BKšœœ œ*œ˜NKšœ˜Kšœ˜K˜—Kšœ˜—Kšœœœ˜SKšœœ5˜Zšœœ˜Kšœ˜š œœ œœ˜0Kšœ0˜0šœ˜ šœ˜Kšœ1˜1Kšœœ˜'Kšœ"œ˜.Kšœ:˜:Kšœ˜—šœ ˜ Kšœ"œ˜CKšœ:˜:Kšœ˜—Kšœœ˜—Kšœ˜—Kšœ˜—š œœœœ#˜nKšœ-˜-šœ˜Kšœ˜šœ œœœ˜2Kšœ*œœ˜MKšœœ.˜JKš œ œœœœT˜†Kšœœ˜(Kšœœ-˜5šœ œ˜Kšœœœœ9˜OK˜(K˜+šœœœ˜šœœ œœ˜Hšœœ˜#Kšœ˜KšœD˜DKšœ˜—Kšœ˜Kšœœ˜Kšœ˜—Kšœœ˜0Kšœ˜—Kšœ˜—Kšœ ˜ Kšœ˜—Kšœ˜—Kšœ-œœœ˜hšœ ˜ Kšœœœœ˜bKšœ˜—Kšœœ˜—Kšœ œœœ˜8Kšœœ.˜QK˜%K˜BK˜ K˜Kšœ7˜7K˜K˜K˜—Kšœœ$˜>Kšœ œ˜&šŸœœœ4œ˜ŒKšœœ$˜:Kšœœ'˜0Kšœ&œ˜-šœœœ˜Kšœ%œ ˜/Kšœ œœ ˜Kšœœ˜ Kšœœ˜ šœ˜Kšœ3˜3Kšœœ ˜Kšœœœ ˜–,[text: TextEdit.RefTextNode, index: INT]šœœœ˜Kš œ œ œœ œœ  œ˜OKšœ  œ  œœ˜&Kšœ ˜,Kšœœ˜—Kšœœœœ˜Kšœ˜Kšœ˜—Kšœ œœ œF˜fKšœ˜—Kšœ˜ K˜K˜—šœ œœ œ˜;K˜—–#[mod: RefTab.SeqIndex _ 21B (17)]˜,K˜—šŸœœ4œœ˜_Kšœœœ˜Kšœn˜nšœœ˜Kšœœœ˜Kšœœœ˜—Kšœ˜K˜—š Ÿ œœœœœœ ˜HKšœœœ˜Kšœœ.˜˜SKš œœœœœœœ˜mK˜3Kšœ9˜9Kšœ0 ˜IKš Ÿœœœœœ:˜ŠšŸœœ˜šŸœœ œ˜Kšœœœœ˜!K˜K˜$Kšœœ˜šŸœœ˜Kšœœ˜Kšœœ|˜ Kšœ ˜ Kšœ˜—šœœœ ˜KšœB˜BK˜?Kšœœ˜6Kš œ œœ œœ˜GKšœ œœ œ˜3šœ!œ˜)Kšœ ˜ šœœœ ˜+šœ˜Kšœ4˜4Kšœ5˜5Kšœ/˜/Kšœ0˜0Kšœ˜—Kšœ ˜—Kš œœ œ œœ˜pKšœ ˜ Kšœ˜—Kšœœ˜Kšœ˜—Kšœ ˜ Kšœ˜—K˜Kšœœ˜Kšœœ˜Kšœ˜—Kšœ˜Kšœ˜K˜—Kšœ œ˜šŸ œœœ>˜VKš œœœœœœœ˜mKšœœ˜ K˜3Kšœ9˜9Kšœ0 ˜IKš Ÿœœœœœ:˜ŠKšœ œ $˜=Kš Ÿœœœœœœ8˜yKšœœ˜Kšœœœ/œ˜dKšœœ$˜Bšœ ˜K˜BK˜?Kšœœ˜6Kšœœœœ˜JKšœ œœ˜Kšœ œœ˜Kšœ œ˜Kšœ œœ˜Kšœœ˜šŸœœœœ˜;K˜&Kšœœ ˜Kšœ+˜+Kšœ œ ˜Kšœœ˜ Kšœœœœœ œ œœ˜Wšœ ˜Kšœ˜Kšœœœ˜$šœ œ˜Kšœ7˜7Kšœœ#˜9Kšœœ!˜7Kšœœœœ˜HKšœ˜—Kšœ œ œœ˜*Kšœœœœ˜EKšœ ˜ Kšœ œœ˜,šœœ˜Kšœ&œL˜uKšœ˜K˜—Kšœœ˜0Kšœ˜—Kšœ ˜ Kšœ˜—šœœ˜#Kšœ%˜%Kšœ˜Kšœ˜—Kšœ œ(˜NKšœ˜šœœ˜KšœH˜HKšœ3˜3Kšœ1˜1Kšœ˜—šœ œ œ˜$K˜Kšœ˜—šœœ˜Kšœ'˜'Kšœ˜—šœœ˜ Kšœ*˜*Kšœ˜—šœ˜#Kšœ,˜0šœ˜KšœœK˜SKšœœ+˜2šœ˜š œ œœœœ˜,KšœFœ˜LKšœ˜K˜—K˜—Kšœ9˜9Kšœ ˜ Kšœ˜——šœ œ˜Kšœ/˜/Kšœ3˜3K˜ Kšœ˜—šœœ˜Kšœ(˜(Kšœ˜—šœœœ˜2Kšœ5˜5Kšœ˜Kšœ˜—šœ œ˜K˜#K˜PK˜Kšœ˜—šœ œ˜K˜#K˜PK˜Kšœ˜—Kšœ˜—K˜Kšœ˜K˜K˜——™šŸœœ˜+Kšœ,˜,šœ!˜!Kš œ œœœœ˜DKšœ˜ —Kšœ˜K˜—š Ÿ œœ%œœ'œœ˜‘Kšœ œ˜(Kšœœ˜%Kšœœœ˜šœœœ˜%Kšœ œ˜.šœœœœ˜9K˜9K˜K˜Kšœ%œ˜BK˜—K˜Kšœ˜—Kšœ?œ˜KK˜K˜—šŸ œœ!˜5Kšœ,˜,šœ ˜ Kš œ œœœœ%˜SKšœ&˜*—Kšœ˜K˜—KšŸœœ*œ˜Fšœ œ˜Kšœ"œœœ ˜Hšœ˜Kš œœœ#œœ˜:Kšœœœœ ˜8KšœœœE˜cšœ˜K˜K˜'K˜!K˜—K˜—K˜K˜—šŸ œœ ˜3Kšœ,˜,šœ˜Kšœ œœ˜(Kšœ+˜/Kšœ+˜/Kšœ˜—Kšœ˜K˜—šŸœœ1œœ˜fKšœ3˜3Kšœ9˜9Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜šŸœœ œ˜"Kšœ œ ˜Kšœ œ ˜Kšœ œ ˜Kšœ œ ˜Kšœ˜—šŸœœœ˜$Kšœ ˜ Kšœ ˜ Kšœ˜—š œœœœ!˜8Kšœ+˜+Kšœ<˜Kšœœ˜—Kšœœ˜ K˜K˜—šœ4˜4K˜—šœ œ˜7Kšœœ˜ K˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ˜Kšœ˜K˜—Kšœœœ˜KšŸœœœœœœœ˜MšŸœœœœ œœœ5˜|K˜—šŸœ˜Kšœ@˜@Kšœ˜ Kšœ˜K˜—Kšœ!˜!Kšœ%˜%šœ'œ˜-K˜——K˜Kšœ˜J˜—…—Ίζω