TEditFormatImpl.mesa
Paxton, January 3, 1983 11:21 am
Maxwell, January 6, 1983 11:30 am
Russ Atkinson, July 25, 1983 6:54 pm
Doug Wyatt, May 22, 1984 11:31:38 am PDT
DIRECTORY
GraphicsOps USING [BitmapRep],
LooksReader USING [Create, Get, Ref, SetPosition],
NameSymbolTable USING [RopeFromName],
NodeStyle USING [ApplyLooks, Copy, Create, FontFace, FontUnderlining, GetBodyIndentI, GetFirstIndentI, GetFontFace, GetFontFamily, GetFontSizeI, GetLeadingI, GetLeftIndentI, GetRightIndentI, GetStrikeout, GetTabLeaderSpacingI, GetTabLocI, GetTabRuleVShiftI, GetTabRuleWeightI, GetTabStopsI, GetTopLeadingI, GetUnderlining, GetVShiftI, Ref, RulesTabCount, RulesTabInfoI, TabAlign, TabStop],
PrincOps USING [BBptr, BBTableSpace],
PrincOpsUtils USING [AlignedBBTable, BITBLT, LongZero],
RopeEdit USING [AlphaNumericChar],
RopeReader USING [Create, Get, ReadOffEnd, Ref, SetPosition],
TEditCompile USING [bitmapWPL, maxLineAscent, maxLineDescent, maxLineHeight],
TEditDocument USING [fatalTiogaError, LineBreak, maxClip, TEditDocumentData],
TEditFormat USING [Bitmap, BitmapRec, FormatInfo, LineInfo, LineInfoRec],
TextEdit USING [GetRope, GetRuns, Offset, RefTextNode, Size],
TextLooks USING [Looks, allLooks, noLooks],
TextNode USING [ForwardClipped, Location, NarrowToTextNode, Ref, StepForward],
VFonts USING [EstablishFont, Font],
ViewerClasses USING [Viewer];
TEditFormatImpl:
MONITOR
IMPORTS LooksReader, NameSymbolTable, NodeStyle, PrincOpsUtils, RopeEdit, RopeReader, TEditDocument, TextEdit, TextNode, VFonts
EXPORTS TEditFormat
SHARES VFonts
= BEGIN OPEN TEditCompile;
endMark: CARDINAL = 32767;
-- edit both GetLineBitmap and GetLineInfo when making formatting changes!!!
GetLineBitmap:
PUBLIC
ENTRY
SAFE PROC [
viewer: ViewerClasses.Viewer, tdd: TEditDocument.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: TEditDocument.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 TEditDocument.fatalTiogaError;
IF (descent ←
MAX[descent, font.height-font.ascent]) >= maxLineDescent
THEN
RETURN WITH ERROR TEditDocument.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
Ignore synthetic bold/italic for this
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];
clear the aux buffer
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 TEditDocument.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
toNextTab: CARDINAL;
tabWidth ← MAX[1, minSpace, NodeStyle.GetTabStopsI[nodeStyle]];
toNextTab ← tabWidth - ((x+extraIndent) MOD tabWidth);
width ← IF toNextTab<minSpace THEN toNextTab+tabWidth ELSE toNextTab;
tabLoc ← x+width;
};
blankPos ← cpos; blankX ← MAX[x, italEndX]; -- take care of italic kerning
tabStart ← x; width ← minSpace;
tabUnderlining ← NodeStyle.GetUnderlining[charStyle];
tabStrikeout ← NodeStyle.GetStrikeout[charStyle];
tabAlignment ← IF tabStop=NIL THEN FlushLeft ELSE tabStop.alignment;
IF x+minSpace > 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;
Redirect blt's to tabBitmap
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]
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];
clear off broken word at end
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: TEditDocument.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 < TEditDocument.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 TEditDocument.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𡤏ont.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
toNextTab: CARDINAL;
tabWidth ← MAX[1, minSpace, NodeStyle.GetTabStopsI[nodeStyle]];
toNextTab ← tabWidth - ((x+extraIndent) MOD tabWidth);
width ← IF toNextTab<minSpace THEN toNextTab+tabWidth ELSE toNextTab;
tabLoc ← x+width;
};
tabStart ← x; tabPtr ← blankWidthPtr ← widthPtr; width ← minSpace;
tabAlignment ← IF tabStop=NIL THEN FlushLeft ELSE tabStop.alignment;
IF x+minSpace > 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 };
END;
IF x + width >= lineWidth THEN EXIT;
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.