1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * MacVim GUI port by Bjorn Winckler
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
11 #import "MMTypesetter.h"
12 #import "MMTextStorage.h"
16 // The 'linerange' functions count U+2028 and U+2029 as line end characters,
17 // which causes rendering to be screwed up because Vim does not count them as
18 // line end characters.
19 #define MM_USE_LINERANGE 0
23 @interface MMTypesetter (Private)
24 - (NSCharacterSet *)hiddenCharSet;
30 @implementation MMTypesetter
33 // Layout glyphs so that each line fragment has a fixed size.
35 // It is assumed that the font for each character has been chosen so that every
36 // glyph has the right advancement (either 2*cellSize.width or half that,
37 // depending on whether it is a wide character or not). This is taken care of
38 // by MMTextStorage in setAttributes:range: and in setFont:. All that is left
39 // for the typesetter to do is to make sure each line fragment has the same
40 // height and that unwanted glyphs are hidden.
42 - (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm
43 startingAtGlyphIndex:(unsigned)startGlyphIdx
44 maxNumberOfLineFragments:(unsigned)maxNumLines
45 nextGlyphIndex:(unsigned *)nextGlyph
47 // TODO: Check that it really is an MMTextStorage.
48 MMTextStorage *ts = (MMTextStorage*)[lm textStorage];
49 NSTextView *tv = [lm firstTextView];
50 NSTextContainer *tc = [tv textContainer];
51 NSFont *font = [ts font];
52 NSString *text = [ts string];
53 unsigned textLen = [text length];
54 NSSize cellSize = [ts cellSize];
55 // NOTE: With non-zero linespace the baseline is adjusted so that the text
56 // is centered within a line.
57 float baseline = [font descender] - floor(.5*[ts linespace]);
59 if (!(ts && tv && tc && font && text && textLen))
62 float baselineOffset = [[NSUserDefaults standardUserDefaults]
63 floatForKey:MMBaselineOffsetKey];
65 baseline += baselineOffset;
67 unsigned startCharIdx = [lm characterIndexForGlyphAtIndex:startGlyphIdx];
68 unsigned i, numberOfLines = 0, firstLine = 0;
69 NSRange firstLineRange = { 0, 0 };
72 // Find the first line and its range, and count the number of lines. (This
73 // info could also be gleaned from MMTextStorage, but we do it here anyway
74 // to make absolutely sure everything is right.)
75 for (i = 0; i < textLen; numberOfLines++) {
76 NSRange lineRange = [text lineRangeForRange:NSMakeRange(i, 0)];
77 if (NSLocationInRange(startCharIdx, lineRange)) {
78 firstLine = numberOfLines;
79 firstLineRange = lineRange;
82 i = NSMaxRange(lineRange);
85 unsigned stride = 1 + [ts actualColumns];
86 numberOfLines = [ts actualRows];
87 firstLine = (unsigned)(startCharIdx/stride);
88 firstLineRange.location = firstLine * stride;
89 unsigned len = [text length] - firstLineRange.location;
90 firstLineRange.length = len < stride ? len : stride;
93 // Perform line fragment generation one line at a time.
94 NSRange lineRange = firstLineRange;
95 unsigned endGlyphIdx = startGlyphIdx;
96 for (i = 0; i < maxNumLines && lineRange.length; ++i) {
97 NSRange glyphRange = [lm glyphRangeForCharacterRange:lineRange
98 actualCharacterRange:nil];
99 NSRect lineRect = { 0, (firstLine+i)*cellSize.height,
100 cellSize.width*(lineRange.length-1), cellSize.height };
101 unsigned endLineIdx = NSMaxRange(lineRange);
102 NSPoint glyphPt = { 0, cellSize.height+baseline };
105 endGlyphIdx = NSMaxRange(glyphRange);
107 [lm setTextContainer:tc forGlyphRange:glyphRange];
108 [lm setLineFragmentRect:lineRect forGlyphRange:glyphRange
110 [lm setLocation:glyphPt forStartOfGlyphRange:glyphRange];
112 // Hide end-of-line and non-zero space characters (there is one after
113 // every wide character).
114 for (j = lineRange.location; j < endLineIdx; ++j) {
115 unichar ch = [text characterAtIndex:j];
116 if (ch == 0x200b || ch == '\n') {
117 NSRange range = { j, 1 };
118 range = [lm glyphRangeForCharacterRange:range
119 actualCharacterRange:nil];
120 [lm setNotShownAttribute:YES forGlyphAtIndex:range.location];
125 lineRange = [text lineRangeForRange:NSMakeRange(endLineIdx, 0)];
127 lineRange.location = endLineIdx;
128 len = [text length] - lineRange.location;
129 if (len < lineRange.length)
130 lineRange.length = len;
135 *nextGlyph = endGlyphIdx;
144 @implementation MMTypesetter (Private)
146 - (NSCharacterSet *)hiddenCharSet
148 static NSCharacterSet *hiddenCharSet = nil;
150 if (!hiddenCharSet) {
151 NSString *string = [NSString stringWithFormat:@"%C\n", 0x200b];
152 hiddenCharSet = [NSCharacterSet
153 characterSetWithCharactersInString:string];
154 [hiddenCharSet retain];
157 return hiddenCharSet;
160 @end // MMTypesetter (Private)