Re: drawGlyphsForGlyphRange layout issue
Re: drawGlyphsForGlyphRange layout issue
- Subject: Re: drawGlyphsForGlyphRange layout issue
- From: Aki Inoue <email@hidden>
- Date: Mon, 24 Sep 2012 11:19:18 -0700
To be compatible with NSLayoutManager, you should use -drawWithRect:options:attributes: here instead of using CT.
Your source of trouble is using -drawAtPoint: which uses NSStringDrawingUsesLineFragmentOrigin option (layout glyphs from the top instead of the glyph origin).
Aki
On 2012/09/24, at 7:02, email@hidden wrote:
>
>
>
> On 24 Sep 2012, at 12:49, email@hidden wrote:
>
>>
>> On 23 Sep 2012, at 17:33, Kyle Sluder <email@hidden> wrote:
>>>
>>> Attributes are specified on a character, rather than glyph, basis. So if
>>> you need to draw your characters with a separate color, you should
>>> probably override
>>> -showCGGlyphs:positions:count:font:matrix:attributes:inContext: to push
>>> and pop the foreground color you want before calling super. Should be a
>>> really simple override, because the context is already set up for you.
>>>
>>>
>> It turns out that customising NSGlyphGenerator is not necessary as the subclass merely calls its delegate, which is the layout manager.
>> So an override on NSLayoutManager of - insertGlyphs:length:forStartingGlyphAtIndex:characterIndex: is appropriate.
>> I cache my substitute glyphs and swap them in as required (a secondary layout manager generates the substitute glyphs).
>>
>> However there are issues.
>>
>> Colourisation in - showCGGlyphs:positions:count:font:matrix:attributes:inContext:
>> will necessitate colouring by glyph value. So if a period is used to highlight a space then all periods in the text get highlighted regardless.
>>
>> Secondly, swapping out the tab glyph for another seems to break the tab functionality in the NSTextView.
>>
>> Also, new line and carriage return glyph substitution doesn't seem to work. The substitute glyph is not drawn.
>> Perhaps its omitted as part of the fragment processing.
>>
>> I imagine that the second issue is related to the type setter, though that's just a guess.
>>
>
> I took another look at NSLayoutManager - drawGlyphsForGlyphRange:atPoint: and decided to try dropping down to CoreText.
> NSString -drawAtPoint:withAttributes: seemed to be the source of trouble and was very inefficient.
>
> I created and cached a CTLineRef for each of my substitute characters thus:
>
> attrString = [[NSAttributedString alloc] initWithString:newLineCharacter attributes:defAttributes];
> textLine = CFMakeCollectable(CTLineCreateWithAttributedString((CFAttributedStringRef)attrString));
> [lineRefs addObject:(id)textLine];
>
> I then used CTLineDraw to draw the required line on demand.
> The vertical alignment of the extra glyphs seems fine with this approach.
>
> - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange atPoint:(NSPoint)containerOrigin
> {
> if (showInvisibleCharacters ) {
>
> NSPoint pointToDrawAt;
> NSRect glyphFragment;
> NSString *completeString = [[self textStorage] string];
> NSInteger lengthToRedraw = NSMaxRange(glyphRange);
>
> void *gcContext = [[NSGraphicsContext currentContext] graphicsPort];
>
> // if our context is flipped then we need to flip our drawn text too
> CGAffineTransform t = {1.0, 0.0, 0.0, -1.0, 0.0, 0.0};
> if (![[NSGraphicsContext currentContext] isFlipped]) {
> t = CGAffineTransformIdentity;
> }
> CGContextSetTextMatrix (gcContext, t);
>
> // we may not have any glyphs generated at this stage
> for (NSInteger idx = glyphRange.location; idx < lengthToRedraw; idx++) {
> unichar characterToCheck = [completeString characterAtIndex:idx];
> NSUInteger lineRefIndex = 0;
>
> if (characterToCheck == '\t') {
> lineRefIndex = 0;
> } else if (characterToCheck == ' ') {
> lineRefIndex = 1;
> } else if (characterToCheck == '\n' || characterToCheck == '\r') {
> lineRefIndex = 2;
> } else {
> continue;
> }
>
> pointToDrawAt = [self locationForGlyphAtIndex:idx];
> glyphFragment = [self lineFragmentRectForGlyphAtIndex:idx effectiveRange:NULL];
> pointToDrawAt.x += glyphFragment.origin.x;
> pointToDrawAt.y += glyphFragment.origin.y;
>
> // get our text line object
> CTLineRef line = (CTLineRef)[lineRefs objectAtIndex:lineRefIndex];
>
> CGContextSetTextPosition(gcContext, pointToDrawAt.x, pointToDrawAt.y);
> CTLineDraw(line, gcContext);
> }
> }
>
> // the following causes glyph generation to occur if required
> [super drawGlyphsForGlyphRange:glyphRange atPoint:containerOrigin];
> }
>
>
> Regards
>
> Jonathan Mitchell
> Mugginsoft LLP
>
>
>
>
>
>
>
>
> _______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden