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"
18 @interface MMTypesetter (Private)
19 - (NSCharacterSet *)hiddenCharSet;
25 @implementation MMTypesetter
28 // Layout glyphs so that each line fragment has a fixed size.
30 // It is assumed that the font for each character has been chosen so that every
31 // glyph has the right advancement (either 2*cellSize.width or half that,
32 // depending on whether it is a wide character or not). This is taken care of
33 // by MMTextStorage in setAttributes:range: and in setFont:. All that is left
34 // for the typesetter to do is to make sure each line fragment has the same
35 // height and that unwanted glyphs are hidden.
37 - (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm
38 startingAtGlyphIndex:(unsigned)startGlyphIdx
39 maxNumberOfLineFragments:(unsigned)maxNumLines
40 nextGlyphIndex:(unsigned *)nextGlyph
42 // TODO: Check that it really is an MMTextStorage.
43 MMTextStorage *ts = (MMTextStorage*)[lm textStorage];
44 NSTextView *tv = [lm firstTextView];
45 NSTextContainer *tc = [tv textContainer];
46 NSFont *font = [ts font];
47 NSString *text = [ts string];
48 unsigned textLen = [text length];
49 NSSize cellSize = [ts cellSize];
50 float baseline = [font descender];
52 if (!(ts && tv && tc && font && text && textLen))
55 float baselineOffset = [[NSUserDefaults standardUserDefaults]
56 floatForKey:MMBaselineOffsetKey];
58 baseline += baselineOffset;
60 unsigned startCharIdx = [lm characterIndexForGlyphAtIndex:startGlyphIdx];
61 unsigned i, numberOfLines = 0, firstLine = 0;
62 NSRange firstLineRange = { 0, 0 };
64 // Find the first line and its range, and count the number of lines. (This
65 // info could also be gleaned from MMTextStorage, but we do it here anyway
66 // to make absolutely sure everything is right.)
67 for (i = 0; i < textLen; numberOfLines++) {
68 NSRange lineRange = [text lineRangeForRange:NSMakeRange(i, 0)];
69 if (NSLocationInRange(startCharIdx, lineRange)) {
70 firstLine = numberOfLines;
71 firstLineRange = lineRange;
74 i = NSMaxRange(lineRange);
77 // Perform line fragment generation one line at a time.
78 NSRange lineRange = firstLineRange;
79 unsigned endGlyphIdx = startGlyphIdx;
80 for (i = 0; i < maxNumLines && lineRange.length; ++i) {
81 NSRange glyphRange = [lm glyphRangeForCharacterRange:lineRange
82 actualCharacterRange:nil];
83 NSRect lineRect = { 0, (firstLine+i)*cellSize.height,
84 cellSize.width*(lineRange.length-1), cellSize.height };
85 unsigned endLineIdx = NSMaxRange(lineRange);
86 NSPoint glyphPt = { 0, cellSize.height+baseline };
89 endGlyphIdx = NSMaxRange(glyphRange);
91 [lm setTextContainer:tc forGlyphRange:glyphRange];
92 [lm setLineFragmentRect:lineRect forGlyphRange:glyphRange
94 [lm setLocation:glyphPt forStartOfGlyphRange:glyphRange];
96 // Hide end-of-line and non-zero space characters (there is one after
97 // every wide character).
98 for (j = lineRange.location; j < endLineIdx; ++j) {
99 unichar ch = [text characterAtIndex:j];
100 if (ch == 0x200b || ch == '\n') {
101 NSRange range = { j, 1 };
102 range = [lm glyphRangeForCharacterRange:range
103 actualCharacterRange:nil];
104 [lm setNotShownAttribute:YES forGlyphAtIndex:range.location];
108 lineRange = [text lineRangeForRange:NSMakeRange(endLineIdx, 0)];
112 *nextGlyph = endGlyphIdx;
121 @implementation MMTypesetter (Private)
123 - (NSCharacterSet *)hiddenCharSet
125 static NSCharacterSet *hiddenCharSet = nil;
127 if (!hiddenCharSet) {
128 NSString *string = [NSString stringWithFormat:@"%C\n", 0x200b];
129 hiddenCharSet = [NSCharacterSet
130 characterSetWithCharactersInString:string];
131 [hiddenCharSet retain];
134 return hiddenCharSet;
137 @end // MMTypesetter (Private)