<> <> <> <> <<>> DIRECTORY GraphicsOps USING [BitmapRep], LooksReader USING [Create, Get, Ref, SetPosition], NameSymbolTable USING [RopeFromName], NodeStyle USING [ApplyLooks, Copy, Create, FontFace, FontUnderlining, GetBodyIndentI, GetFirstIndentI, GetFontFace, GetLeadingI, GetLeftIndentI, GetRightIndentI, GetFontFamily, GetFontSizeI, GetStrikeout, GetTabLeaderSpacingI, GetTabRuleWeightI, GetTabRuleVShiftI, RulesTabCount, RulesTabInfoI, GetTabLocI, GetTabStopsI, GetTopLeadingI, GetUnderlining, GetVShiftI, Ref, TabAlign, TabStop], RopeEdit USING [AlphaNumericChar], RopeReader USING [Create, Get, ReadOffEnd, Ref, SetPosition], PrincOps USING [BBptr, BBTableSpace], PrincOpsUtils USING [AlignedBBTable, BITBLT, LongZero], TextEdit USING [GetRope, GetRuns, Offset, RefTextNode, Size], TextLooks USING [Looks, allLooks, noLooks], TextNode USING [ForwardClipped, Location, NarrowToTextNode, Ref, StepForward], TEditCompile USING [bitmapWPL, maxLineAscent, maxLineDescent, maxLineHeight], TEditDocument USING [fatalTiogaError, LineBreak, maxClip, TEditDocumentData], TEditFormat, VFonts USING [EstablishFont, Font], ViewerClasses USING [Viewer]; TEditFormatImpl: MONITOR IMPORTS PrincOpsUtils, LooksReader, NameSymbolTable, NodeStyle, RopeEdit, RopeReader, TEditDocument, TextEdit, TextNode, VFonts EXPORTS TEditFormat SHARES VFonts = BEGIN OPEN TEditDocument, TEditCompile; endMark: CARDINAL = 32767; <<-- edit both GetLineBitmap and GetLineInfo when making formatting changes!!!>> GetLineBitmap: PUBLIC ENTRY SAFE PROC [viewer: ViewerClasses.Viewer, tdd: TEditDocumentData, node: TextEdit.RefTextNode, pos: TextEdit.Offset, formatInfo: TEditFormat.FormatInfo, trimEnd: BOOLEAN _ FALSE] RETURNS [bitmap: TEditFormat.Bitmap] = TRUSTED BEGIN OPEN formatInfo; ENABLE UNWIND => NULL; cpos, blankPos: TextEdit.Offset _ pos; char: CHARACTER; width: CARDINAL; tabStop: NodeStyle.TabStop; tabNumber: CARDINAL _ 0; -- tells us which tab stop to use tabUnderlining: NodeStyle.FontUnderlining; tabStrikeout: NodeStyle.FontUnderlining; tabLoc, tabStart, tabWidth, tabTextStart: CARDINAL; tabAlignment: NodeStyle.TabAlign _ FlushLeft; tabAlignmentChar: CHAR _ 0C; x, italEndX, italExtra: CARDINAL _ 0; ascent: CARDINAL _ font.ascent; descent: CARDINAL _ font.height - font.ascent; bltYOffset: LONG POINTER; blankX: CARDINAL _ endMark; looks, tabCharLooks, prevTabLooks: TextLooks.Looks _ TextLooks.noLooks; legalChar: BOOL; lineWidth, lineStart, extraIndent: INTEGER; break: LineBreak; bitmap: REF _ oneLineBitmap; bitmapOffset: CARDINAL; doingTab: BOOL _ FALSE; NewLooks: PROC = { NodeStyle.Copy[dest: charStyle, source: tabStyle]; -- back to the basic style IF looks#TextLooks.noLooks THEN NodeStyle.ApplyLooks[charStyle, looks]; font _ GetFont[charStyle]; IF (ascent _ MAX[ascent, font.ascent]) > maxLineAscent THEN RETURN WITH ERROR fatalTiogaError; IF (descent _ MAX[descent, font.height-font.ascent]) >= maxLineDescent THEN RETURN WITH ERROR fatalTiogaError; bitmapOffset _ (maxLineAscent-font.ascent-NodeStyle.GetVShiftI[charStyle])*bitmapWPL; bltYOffset _ LOOPHOLE[bitmap, LONG POINTER] + bitmapOffset; cbbp.height _ font.height; cbbp.srcDesc.srcBpl _ font.raster*16; IF font.synthItalic THEN BEGIN italSlant: CARDINAL = 3; italPieces _ (font.height+italSlant-1)/italSlant; italSlice _ (font.height+italPieces-1)/italPieces; italRem _ font.height MOD italSlice; IF italRem=0 THEN italRem _ italSlice; italInitDx _ 1 - ((italPieces+1)/2); italDy _ italSlice*bitmapWPL; italInitDy _ italPieces*italDy; italFdy _ italSlice*font.raster; italInitFdy _ italPieces*italFdy; italExtra _ italPieces+italInitDx END; cbbp.flags.dstFunc _ IF font.synthBold THEN or ELSE null; savedLooks _ looks }; TabFill: PROC = { TabRule: PROC [weight, vshift: INTEGER] = { bltYOffset: LONG POINTER = LOOPHOLE[oneLineBitmap, LONG POINTER] + ((maxLineAscent-vshift-weight+1)*bitmapWPL); ubbp.width _ tabWidth; ubbp.dst _ [LOOPHOLE[bltYOffset+(tabStart/16)],,tabStart MOD 16]; ubbp.height _ MAX[1,weight]; PrincOpsUtils.BITBLT[ubbp]; ubbp.height _ 1 }; saveX: CARDINAL _ x; saveWidth: CARDINAL _ width; x _ tabStart; width _ tabWidth; IF tabCharLooks#savedLooks THEN { looks _ tabCharLooks; NewLooks[] }; IF tabUnderlining=All THEN Underline[]; IF tabStrikeout=All THEN Strikeout[]; IF tabStop#NIL THEN WITH ts:tabStop SELECT FROM blank => NULL; leaders => { charWidth: CARDINAL _ font.width[ts.char]; spacing: INTEGER _ MAX[charWidth, NodeStyle.GetTabLeaderSpacingI[@ts, nodeStyle]]; leaderStart: INTEGER _ IF ts.congruent THEN ((tabStart+lineStart+spacing-1)/spacing)*spacing - lineStart ELSE tabStart + (tabWidth-(tabWidth/spacing)*spacing)/2; leaderQuit: INTEGER _ tabStart+tabWidth-spacing; src: CARDINAL _ font.xInSegment[ts.char]; extra: CARDINAL _ (spacing-charWidth)/2; leaderStart _ leaderStart + extra; -- even out the extra space leaderQuit _ leaderQuit + extra; cbbp.flags.dstFunc _ or; -- to take care of kerning cbbp.width _ charWidth; cbbp.src _ [font.bitmap+(src/16),,src MOD 16]; FOR xx: INTEGER _ leaderStart, xx+spacing UNTIL xx > leaderQuit DO <> cbbp.dst _ [LOOPHOLE[bltYOffset+(xx/16)],,xx MOD 16]; PrincOpsUtils.BITBLT[cbbp]; ENDLOOP; cbbp.flags.dstFunc _ null }; rule => { weight: INTEGER _ NodeStyle.GetTabRuleWeightI[@ts, nodeStyle]; vshift: INTEGER _ NodeStyle.GetTabRuleVShiftI[@ts, nodeStyle]; TabRule[weight, vshift] }; rules => { FOR i: INTEGER IN [0..NodeStyle.RulesTabCount[@ts]) DO weight, vshift: INTEGER; [weight, vshift] _ NodeStyle.RulesTabInfoI[@ts, i]; TabRule[weight, vshift]; ENDLOOP }; ENDCASE => ERROR; x _ saveX; width _ saveWidth }; FinishTab: PROC = { yOffset: CARDINAL = (maxLineAscent-ascent)*bitmapWPL; textWidth: CARDINAL _ x-tabTextStart; distanceToMove, dest, width: CARDINAL; alignmentPoint: CARDINAL _ SELECT tabAlignment FROM FlushRight, Character => x, -- move right end of text to tabLoc Centered => (x+tabTextStart)/2, -- move center of text to tabLoc ENDCASE => ERROR; -- handle FlushLeft elsewhere IF alignmentPoint >= tabLoc THEN distanceToMove _ 0 -- too much text to fit; leave it alone ELSE { distanceToMove _ MIN[tabLoc-alignmentPoint, lineWidth-x]; x _ x+distanceToMove }; tabWidth _ distanceToMove+tabTextStart-tabStart; dest _ tabStart+distanceToMove; width _ tabTextStart-tabStart+textWidth+ascent--for italic kerning--; tbbp.width _ MIN[bitmapWPL*16, dest+width]-dest; tbbp.height _ ascent+descent; tbbp.src _ [LOOPHOLE[bltYOffset+(tabStart/16)],,tabStart MOD 16]; bltYOffset _ LOOPHOLE[bitmap _ oneLineBitmap, LONG POINTER] + bitmapOffset; tbbp.dst _ [LOOPHOLE[bltYOffset+(dest/16)],,dest MOD 16]; PrincOpsUtils.BITBLT[tbbp]; <> TrimLineBuffer[tabBitmap, tabStart, tabStart+width, ascent, descent]; TabFill[]; doingTab _ FALSE }; Underline: PROC = INLINE BEGIN bltYOffset: LONG POINTER = LOOPHOLE[bitmap, LONG POINTER] + ((maxLineAscent+1)*bitmapWPL); ubbp.width _ width; ubbp.dst _ [LOOPHOLE[bltYOffset+(x/16)],,x MOD 16]; PrincOpsUtils.BITBLT[ubbp]; END; Strikeout: PROC = INLINE BEGIN bltYOffset: LONG POINTER = LOOPHOLE[bitmap, LONG POINTER] + ((maxLineAscent-(font.ascent/2))*bitmapWPL); ubbp.width _ width; ubbp.dst _ [LOOPHOLE[bltYOffset+(x/16)],,x MOD 16]; PrincOpsUtils.BITBLT[ubbp]; END; IF cpos > TextEdit.Size[node] THEN RETURN WITH ERROR fatalTiogaError; extraIndent _ IF cpos = 0 THEN firstIndent ELSE bodyIndent; lineWidth _ lineMeasure - extraIndent; lineStart _ leftIndent + extraIndent; NodeStyle.Copy[dest: tabStyle, source: nodeStyle]; -- initialize tabStyle to be equal to nodeStyle savedLooks _ TextLooks.allLooks; -- force initial call on NewLooks RopeReader.SetPosition[ropeReader, TextEdit.GetRope[node], cpos]; LooksReader.SetPosition[looksReader, TextEdit.GetRuns[node], cpos]; cbbp.height _ font.height; cbbp.srcDesc.srcBpl _ font.raster*16; ClearLineBuffer[oneLineBitmap]; DO char _ RopeReader.Get[ropeReader ! RopeReader.ReadOffEnd => {break _ eon; EXIT}]; IF (looks_LooksReader.Get[looksReader])#savedLooks AND char#11C THEN NewLooks[]; width _ IF (legalChar _ char IN [font.min..font.max]) THEN font.width[char] ELSE MAX[3, font.width[40C]]; SELECT char FROM 40C => BEGIN blankX _ MAX[x, italEndX]; -- take care of italic kerning blankPos _ cpos; -- to back up to for word breaks IF NodeStyle.GetUnderlining[charStyle]=All THEN Underline[]; IF NodeStyle.GetStrikeout[charStyle]=All THEN Strikeout[]; END; 15C => {break _ cr; EXIT}; -- end of line 11C => BEGIN tabLooks: TextLooks.Looks; minSpace: CARDINAL; IF doingTab THEN FinishTab[]; -- finish previous tab before start this one tabNumber _ tabNumber+1; IF tabNumber > nodeStyle.numTabStops THEN { IF (tabStop _ nodeStyle.defaultTabStops) # NIL THEN { tabLoc _ IF nodeStyle.tabStops=NIL THEN 0 ELSE NodeStyle.GetTabLocI[nodeStyle.tabStops.first, nodeStyle]; tabLoc _ tabLoc + (tabNumber-nodeStyle.numTabStops)* NodeStyle.GetTabLocI[tabStop, nodeStyle] } ELSE NULL -- calculate tabLoc after establish looks -- } ELSE { -- go down list until find the proper tab stop tabStops: LIST OF NodeStyle.TabStop _ nodeStyle.tabStops; FOR i: CARDINAL _ nodeStyle.numTabStops, i-1 UNTIL i=tabNumber DO tabStops _ tabStops.rest; ENDLOOP; tabStop _ tabStops.first; tabLoc _ NodeStyle.GetTabLocI[tabStop, nodeStyle] }; IF nodeStyle.fixedTabs THEN tabLoc _ tabLoc-lineStart; tabLooks _ IF tabStop = NIL THEN TextLooks.noLooks ELSE tabStop.looks; IF tabLooks # prevTabLooks THEN { NodeStyle.Copy[dest: tabStyle, source: nodeStyle]; IF tabLooks#TextLooks.noLooks THEN NodeStyle.ApplyLooks[tabStyle, tabLooks]; prevTabLooks _ tabLooks; NewLooks[] } ELSE IF looks#savedLooks THEN NewLooks[]; tabCharLooks _ looks; minSpace _ font.width[40C]; -- this must come after establish looks IF tabStop=NIL THEN { -- pick value [spaceWidth..tabWidth+spaceWidth) for tab width maxSpace, toNextTab: CARDINAL; tabWidth _ NodeStyle.GetTabStopsI[nodeStyle]; maxSpace _ tabWidth*minSpace; toNextTab _ maxSpace - ((x+extraIndent) MOD maxSpace); width _ IF toNextTab tabLoc THEN { -- we are beyond the tab stop IF tabStop=NIL OR ~tabStop.breakIfPast THEN { -- treat like a space tabWidth _ minSpace; TabFill[] } ELSE -- force line break -- {break _ wrap; EXIT}} ELSE IF tabAlignment=FlushLeft THEN { -- simple case; finish now tabWidth _ width _ tabLoc-x; TabFill[] } ELSE { -- will finish after have the text to be positioned doingTab _ TRUE; tabAlignmentChar _ IF tabAlignment=Character THEN tabStop.alignmentChar ELSE 0C; tabTextStart _ tabStart+width; <> bltYOffset _ LOOPHOLE[bitmap _ tabBitmap, LONG POINTER] + bitmapOffset }; END; ENDCASE => BEGIN IF char=tabAlignmentChar AND tabAlignment=Character AND doingTab THEN FinishTab[]; IF x+width >= LOOPHOLE[lineWidth, CARDINAL] THEN {break _ wrap; EXIT}; IF ~legalChar THEN BEGIN -- illegal char for display font color: CARDINAL _ 177777B; oldsrcDesc: INTEGER _ cbbp.srcDesc.srcBpl; cbbp.height _ font.ascent; cbbp.width _ width-2; -- hack some white space between bad chars cbbp.dst _ [LOOPHOLE[bltYOffset+((x+1)/16)],,(x+1) MOD 16]; cbbp.flags.gray _ TRUE; cbbp.srcDesc.gray _ [0, 0, 0, 0]; cbbp.src _ [@color,,0]; PrincOpsUtils.BITBLT[cbbp]; cbbp.height _ font.height; cbbp.flags.gray _ FALSE; cbbp.srcDesc.srcBpl _ oldsrcDesc; END ELSE BEGIN src: CARDINAL _ font.xInSegment[char]; cbbp.width _ width; IF font.synthItalic THEN BEGIN cbbp.src _ [font.bitmap+(src/16)+italInitFdy,,src MOD 16]; IF font.synthBold THEN BEGIN --bolditalic synth tx: INTEGER _ x + italInitDx; ty: CARDINAL _ italInitDy; cbbp.height _ italRem; THROUGH [0..italPieces) DO cbbp.src.word _ cbbp.src.word - italFdy; ty _ ty - italDy; cbbp.dst _ [LOOPHOLE[bltYOffset+(MAX[0, tx]/16)+ty],,MAX[0, tx] MOD 16]; PrincOpsUtils.BITBLT[cbbp]; tx _ tx+1; cbbp.dst _ [LOOPHOLE[bltYOffset+(MAX[0, tx]/16)+ty],,MAX[0, tx] MOD 16]; PrincOpsUtils.BITBLT[cbbp]; cbbp.height _ italSlice; ENDLOOP; width _ width+1; -- compensate for extra bit displayed END ELSE BEGIN -- italic synth tx: INTEGER _ x + italInitDx; ty: CARDINAL _ italInitDy; cbbp.height _ italRem; THROUGH [0..italPieces) DO cbbp.src.word _ cbbp.src.word - italFdy; ty _ ty - italDy; cbbp.dst _ [LOOPHOLE[bltYOffset+(MAX[0, tx]/16)+ty],,MAX[0, tx] MOD 16]; PrincOpsUtils.BITBLT[cbbp]; tx _ tx + 1; cbbp.height _ italSlice; ENDLOOP; END; italEndX _ x+width+italExtra; END ELSE IF font.synthBold THEN BEGIN -- bold synth cbbp.src _ [font.bitmap+(src/16),,src MOD 16]; cbbp.dst _ [LOOPHOLE[bltYOffset+(x/16)],,x MOD 16]; PrincOpsUtils.BITBLT[cbbp]; x _ x+1; -- compensate for extra bit displayed cbbp.dst _ [LOOPHOLE[bltYOffset+(x/16)],,x MOD 16]; PrincOpsUtils.BITBLT[cbbp]; END ELSE BEGIN -- normal face cbbp.src _ [font.bitmap+(src/16),,src MOD 16]; cbbp.dst _ [LOOPHOLE[bltYOffset+(x/16)],,x MOD 16]; PrincOpsUtils.BITBLT[cbbp]; END; END; SELECT NodeStyle.GetUnderlining[charStyle] FROM All, Visible => Underline[]; LettersAndDigits => IF RopeEdit.AlphaNumericChar[char] THEN Underline[]; ENDCASE; SELECT NodeStyle.GetStrikeout[charStyle] FROM All, Visible => Strikeout[]; LettersAndDigits => IF RopeEdit.AlphaNumericChar[char] THEN Strikeout[]; ENDCASE; END; cpos _ cpos+1; x _ x+width; ENDLOOP; <<-- we fell out of the loop because we got to the right edge or because>> <<-- we hit a logical end-of-line (return or end-of-node)>> <<------ see if need to finish a tab>> IF doingTab THEN FinishTab[]; <<>> <<------ fix up the character position for start of next line>> IF break=cr OR break=eon THEN cpos _ cpos+1 ELSE IF blankX#endMark THEN cpos _ blankPos+1; <<-- add one to avoid blank at start of next line>> <<------ here do centering, flushing etc.>> SELECT lineFormatting FROM FlushLeft, Justified => NULL; FlushRight => lineStart _ lineStart + (lineWidth-x); Centered => lineStart _ lineStart + (lineWidth-x)/2; ENDCASE => ERROR; width _ IF break=cr THEN x+width ELSE IF break=eon OR blankX=endMark THEN MAX[x, italEndX] ELSE blankX; formatBitmap^ _ [ bits: formatBitmapRep, yOffset: maxLineAscent-ascent, ascent: ascent, descent: descent, width: width, chars: cpos-pos, leftIndent: lineStart, break: break ]; savedAscent _ ascent; savedDescent _ descent; -- so can clear line buffer next time IF trimEnd AND x#width THEN TrimLineBuffer[bitmap, width, x, ascent, descent]; <> RETURN [formatBitmap]; END; savedAscent: CARDINAL _ maxLineAscent; savedDescent: CARDINAL _ maxLineDescent; ClearLineBuffer: PROC [bitmap: REF] = INLINE { IF savedAscent#0 THEN BEGIN [] _ PrincOpsUtils.LongZero[LOOPHOLE[bitmap, LONG POINTER TO UNSPECIFIED] + ((maxLineAscent-savedAscent)*bitmapWPL), (savedAscent+savedDescent)*bitmapWPL]; savedAscent _ savedDescent _ 0; END; }; TrimLineBuffer: PROC [bitmap: REF, start, end, ascent, descent: CARDINAL] = INLINE { bltYOffset: LONG POINTER = LOOPHOLE[bitmap, LONG POINTER] + ((maxLineAscent-ascent)*bitmapWPL); bbbp.height _ ascent+descent; bbbp.width _ MIN[bitmapWPL*16, end+1+ascent--for italic kerning--]-start; bbbp.dst _ [LOOPHOLE[bltYOffset+(start/16)],,start MOD 16]; PrincOpsUtils.BITBLT[bbbp]; }; globalLineInfo: TEditFormat.LineInfo _ NEW[TEditFormat.LineInfoRec]; GetLineInfo: PUBLIC ENTRY SAFE PROC [viewer: ViewerClasses.Viewer, tdd: TEditDocumentData, pos: TextNode.Location, nodeStyle: NodeStyle.Ref] RETURNS [lineInfo: TEditFormat.LineInfo, ascent: INTEGER, descent: INTEGER, nextPos: TextNode.Location, nChars: CARDINAL, leading: INTEGER] = TRUSTED BEGIN ENABLE UNWIND => NULL; cpos: TextEdit.Offset _ pos.where; node: TextEdit.RefTextNode _ TextNode.NarrowToTextNode[pos.node]; tabStop: NodeStyle.TabStop; tabNumber: CARDINAL _ 0; -- tells us which tab stop to use tabLoc, tabStart, tabWidth, tabTextStart: CARDINAL; tabAlignment: NodeStyle.TabAlign _ FlushLeft; tabAlignmentChar: CHAR _ 0C; level: INTEGER _ 0; -- in case we are doing level clipping maxLevel: INTEGER _ tdd.clipLevel; levelClipping: BOOLEAN _ maxLevel < maxClip; char: CHARACTER; width: CARDINAL; font: VFonts.Font; extraIndent: INTEGER = (IF cpos = 0 THEN NodeStyle.GetFirstIndentI[nodeStyle] ELSE NodeStyle.GetBodyIndentI[nodeStyle]); leftIndent: INTEGER = NodeStyle.GetLeftIndentI[nodeStyle] + extraIndent; eon: BOOLEAN _ FALSE; lineWidth: CARDINAL = MAX[0, viewer.cw - NodeStyle.GetRightIndentI[nodeStyle] - leftIndent]; x, widthPtr, italPtr, tabPtr, italEndX, italExtra: CARDINAL _ 0; blankWidthPtr: CARDINAL _ endMark; savedLooks: TextLooks.Looks _ TextLooks.allLooks; -- to force init doingTab: BOOL _ FALSE; looks, tabCharLooks, prevTabLooks: TextLooks.Looks _ TextLooks.noLooks; NewLooks: PROC = { NodeStyle.Copy[dest: lineInfoCharStyle, source: lineInfoTabStyle]; IF looks#TextLooks.noLooks THEN NodeStyle.ApplyLooks[lineInfoCharStyle, looks]; font _ GetFont[lineInfoCharStyle]; IF font.synthItalic THEN BEGIN italSlant: CARDINAL = 3; pieces: CARDINAL _ (font.height+italSlant-1)/italSlant; init: INTEGER _ 1 - ((pieces+1)/2); italExtra _ pieces+init; END; savedLooks _ looks; ascent _ MAX[ascent, font.ascent]; descent _ MAX[descent, font.height-font.ascent] }; FinishTab: PROC = { textWidth: CARDINAL _ x-tabTextStart; distanceToMove: CARDINAL; alignmentPoint: CARDINAL _ SELECT tabAlignment FROM FlushRight, Character => x, -- move right end of text to tabLoc Centered => (x+tabTextStart)/2, -- move center of text to tabLoc ENDCASE => ERROR; -- handle FlushLeft elsewhere IF alignmentPoint >= tabLoc THEN distanceToMove _ 0 -- too much text to fit; leave it alone ELSE { distanceToMove _ MIN[tabLoc-alignmentPoint, lineWidth-x]; x _ x+distanceToMove }; tabWidth _ distanceToMove+tabTextStart-tabStart; lineInfo[tabPtr] _ tabWidth; doingTab _ FALSE }; lineInfo _ globalLineInfo; -- better performance if this isn't allocated each time IF node=NIL OR cpos > TextEdit.Size[node] THEN RETURN WITH ERROR fatalTiogaError; NodeStyle.Copy[dest: lineInfoTabStyle, source: nodeStyle]; RopeReader.SetPosition[ropeReader, TextEdit.GetRope[node], cpos]; LooksReader.SetPosition[looksReader, TextEdit.GetRuns[node], cpos]; ascent _ descent _ 0; DO char _ RopeReader.Get[ropeReader ! RopeReader.ReadOffEnd => {eon _ TRUE; EXIT}]; IF (looks_LooksReader.Get[looksReader])#savedLooks THEN NewLooks[]; width _ IF char IN [font.min..font.max] THEN font.width[char] ELSE MAX[3, font.width[40C]]; SELECT char FROM 40C => {width_font.width[' ]; blankWidthPtr _ widthPtr}; 11C => BEGIN -- tab tabLooks: TextLooks.Looks; minSpace: CARDINAL; IF doingTab THEN FinishTab[]; -- finish previous tab before start this one tabNumber _ tabNumber+1; IF tabNumber > nodeStyle.numTabStops THEN { IF (tabStop _ nodeStyle.defaultTabStops) # NIL THEN { tabLoc _ IF nodeStyle.tabStops=NIL THEN 0 ELSE NodeStyle.GetTabLocI[nodeStyle.tabStops.first, nodeStyle]; tabLoc _ tabLoc + (tabNumber-nodeStyle.numTabStops)* NodeStyle.GetTabLocI[tabStop, nodeStyle] } ELSE NULL -- calculate tabLoc after establish looks -- } ELSE { -- go down list until find the proper tab stop tabStops: LIST OF NodeStyle.TabStop _ nodeStyle.tabStops; FOR i: CARDINAL _ nodeStyle.numTabStops, i-1 UNTIL i=tabNumber DO tabStops _ tabStops.rest; ENDLOOP; tabStop _ tabStops.first; tabLoc _ NodeStyle.GetTabLocI[tabStop, nodeStyle] }; IF nodeStyle.fixedTabs THEN tabLoc _ tabLoc-leftIndent; tabLooks _ IF tabStop = NIL THEN TextLooks.noLooks ELSE tabStop.looks; IF tabLooks # prevTabLooks THEN { NodeStyle.Copy[dest: lineInfoTabStyle, source: nodeStyle]; IF tabLooks#TextLooks.noLooks THEN NodeStyle.ApplyLooks[lineInfoTabStyle, tabLooks]; prevTabLooks _ tabLooks; NewLooks[] } ELSE IF looks#savedLooks THEN NewLooks[]; tabCharLooks _ looks; minSpace _ font.width[40C]; -- this must come after establish looks IF tabStop=NIL THEN { -- pick value [spaceWidth..tabWidth+spaceWidth) for tab width maxSpace, toNextTab: CARDINAL; tabWidth _ NodeStyle.GetTabStopsI[nodeStyle]; maxSpace _ tabWidth*minSpace; toNextTab _ maxSpace - ((x+extraIndent) MOD maxSpace); width _ IF toNextTab tabLoc THEN { -- we are beyond the tab stop IF tabStop=NIL OR ~tabStop.breakIfPast THEN { -- treat like a space tabWidth _ minSpace } ELSE -- force line break -- EXIT} ELSE IF tabAlignment=FlushLeft THEN { -- simple case; finish now tabWidth _ width _ tabLoc-x } ELSE { -- will finish after have the text to be positioned doingTab _ TRUE; tabAlignmentChar _ IF tabAlignment=Character THEN tabStop.alignmentChar ELSE 0C; tabTextStart _ tabStart+width }; END ENDCASE => BEGIN IF char=tabAlignmentChar AND tabAlignment=Character AND doingTab THEN FinishTab[]; IF font.synthBold THEN width _ width+1; IF font.synthItalic THEN { italEndX _ x+width+italExtra; italPtr _ widthPtr }; IF x + width >= lineWidth THEN EXIT; END; lineInfo[widthPtr] _ width; widthPtr _ widthPtr+1; cpos _ cpos+1; x _ x+width; IF char=15C THEN EXIT; -- eol ENDLOOP; <<-- we fell out of the loop because we got to the right edge or because>> <<-- we hit a logical end-of-line (return or end-of-node)>> <<------ see if need to finish a tab>> IF doingTab THEN FinishTab[]; IF italEndX > x THEN lineInfo[italPtr] _ lineInfo[italPtr]+italExtra; -- end of line italic kerning IF char=15C OR eon OR blankWidthPtr=endMark THEN lineInfo[widthPtr] _ endMark ELSE BEGIN lineInfo[blankWidthPtr+1] _ endMark; cpos _ pos.where+blankWidthPtr+1; END; IF cpos>=TextEdit.Size[node] THEN BEGIN n: TextNode.Ref; IF levelClipping THEN [n,level] _ TextNode.ForwardClipped[node,maxLevel,level] ELSE n _ TextNode.StepForward[node]; nextPos _ [n,0]; nChars _ TextEdit.Size[node] - pos.where; END ELSE BEGIN nextPos _ [node, cpos]; nChars _ cpos - pos.where; END; leading _ IF pos.where=0 THEN NodeStyle.GetTopLeadingI[nodeStyle] ELSE NodeStyle.GetLeadingI[nodeStyle]; END; GetFont: PROC [style: NodeStyle.Ref] RETURNS [font: VFonts.Font] = INLINE BEGIN face: NodeStyle.FontFace _ NodeStyle.GetFontFace[style]; font _ VFonts.EstablishFont[ NameSymbolTable.RopeFromName[NodeStyle.GetFontFamily[style]], NodeStyle.GetFontSizeI[style], face=Bold OR face=BoldItalic, face=Italic OR face=BoldItalic ]; END; AllocateBitmap: PROC [nWords: CARDINAL] RETURNS [REF] = BEGIN Words: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF CARDINAL]; RETURN[NEW[Words[nWords]]]; END; <<-- Main Program>> bbbTable, cbbTable, tbbTable, ubbTable: PrincOps.BBTableSpace; bbbp: PrincOps.BBptr _ PrincOpsUtils.AlignedBBTable[@bbbTable]; -- buffer end trimming cbbp: PrincOps.BBptr _ PrincOpsUtils.AlignedBBTable[@cbbTable]; -- chars to line buffer tbbp: PrincOps.BBptr _ PrincOpsUtils.AlignedBBTable[@tbbTable]; -- chars to tab line buffer ubbp: PrincOps.BBptr _ PrincOpsUtils.AlignedBBTable[@ubbTable]; -- underline and strikeout oneLineBitmap: REF _ AllocateBitmap[bitmapWPL*maxLineHeight]; <<-- line buffer; magic storage provided by AllocateBitmap>> tabBitmap: REF _ AllocateBitmap[bitmapWPL*maxLineHeight]; <<-- line buffer for tab chars; magic storage provided by AllocateBitmap>> formatBitmapRep: REF GraphicsOps.BitmapRep _ NEW[GraphicsOps.BitmapRep _ [ base: oneLineBitmap, raster: bitmapWPL, width: bitmapWPL*16, height: maxLineHeight ]]; formatBitmap: TEditFormat.Bitmap _ NEW[TEditFormat.BitmapRec _ [ bits: formatBitmapRep, chars:, leftIndent: ]]; myWhiteWord: CARDINAL _ 0; -- white for erasing bitmap buffers myBlackWord: CARDINAL _ 177777B; -- black for underlining and strikeout <<-- RopeReader and LooksReader Stuff>> ropeReader: RopeReader.Ref _ RopeReader.Create[]; looksReader: LooksReader.Ref _ LooksReader.Create[]; lineInfoCharStyle: NodeStyle.Ref _ NodeStyle.Create[]; lineInfoTabStyle: NodeStyle.Ref _ NodeStyle.Create[]; bbbp.flags.disjoint _ TRUE; bbbp.flags.disjointItems _ TRUE; bbbp.flags.gray _ TRUE; bbbp.srcDesc.gray _ [0, 0, 0, 0]; bbbp.src _ [@myWhiteWord,,0]; bbbp.dstBpl _ bitmapWPL*16; cbbp.flags.disjoint _ TRUE; cbbp.flags.disjointItems _ TRUE; cbbp.dstBpl _ bitmapWPL*16; tbbp.flags.disjoint _ TRUE; tbbp.flags.disjointItems _ TRUE; tbbp.flags.dstFunc _ or; -- to take care of kerning tbbp.srcDesc.srcBpl _ bitmapWPL*16; tbbp.dstBpl _ bitmapWPL*16; ubbp.flags.disjoint _ TRUE; ubbp.flags.disjointItems _ TRUE; ubbp.flags.gray _ TRUE; ubbp.srcDesc.gray _ [0, 0, 0, 0]; ubbp.src _ [@myBlackWord,,0]; ubbp.dstBpl _ bitmapWPL*16; ubbp.height _ 1; ClearLineBuffer[oneLineBitmap]; savedAscent _ maxLineAscent; savedDescent _ maxLineDescent; ClearLineBuffer[tabBitmap]; END.