Thank you immensely, Douglas. I've now got something working if I need to insert one or two glyphs, but it crashes on three or more glyphs.

You're right, I need to fix my code for invoking the actual glyph replacement. For now, let's assume that the attributedString contains only one character with an attribute designating it as a dynamic text marker. (There will always be only one character per marker).

I'm not 100% sure which method to override in the typesetter, but I assume something like this is appropriate (viz., insert or replace glyphs in layout manager and then call super):

- (NSUInteger)getGlyphsInRange:(NSRange)glyphsRange glyphs:(NSGlyph *)glyphBuffer characterIndexes:(NSUInteger *)charIndexBuffer glyphInscriptions:(NSGlyphInscription *)inscribeBuffer elasticBits: (BOOL *)elasticBuffer bidiLevels:(unsigned char *)bidiLevelBuffer { NSRange charRange = [layoutManager characterRangeForGlyphRange:glyphsRange actualGlyphRange:NULL];
        NSRange effRange = NSMakeRange(charRange.location, 0);
        while (NSMaxRange(effRange) < NSMaxRange(charRange)) {
                NSUInteger charIndex = NSMaxRange(effRange);
NSNumber *dynText = [attributedString attribute:kDynamicTextAttributeName atIndex:charIndex effectiveRange:&effRange];
                if (dynText != nil) {
// Grab the glyph range for the char; it might already be correct length. NSRange dynTextGlyphRange = [layoutManager glyphRangeForCharacterRange:NSMakeRange(effRange.location, 1) actualCharacterRange:NULL];
                        // for testing, use an arbitrary array of glyphs:
                        NSUInteger newGlyphsLength = 2;
                        NSUInteger newGlyphs[2] = {59, 60};
                        NSUInteger i;
                        for (i = 0; i < newGlyphsLength; i++) {
                                if (i < dynTextGlyphRange.length) {
[layoutManager replaceGlyphAtIndex:(dynTextGlyphRange.location + i) withGlyph:newGlyphs[i]];
                                } else {
[layoutManager insertGlyph:newGlyphs[i] atGlyphIndex: (dynTextGlyphRange.location + i) characterIndex:effRange.location]; //[layoutManager setCharacterIndex:effRange.location forGlyphAtIndex:(dynTextGlyphRange.location + i)];
                                }
                        }
                }
        }
return [super getGlyphsInRange:(NSRange)glyphsRange glyphs:(NSGlyph *)glyphBuffer characterIndexes:(NSUInteger *)charIndexBuffer glyphInscriptions:(NSGlyphInscription *)inscribeBuffer elasticBits: (BOOL *)elasticBuffer bidiLevels:(unsigned char *)bidiLevelBuffer];
}

This works fine, as I said, unless newGlyphsLength > 2. The call to setCharacterIndex:forGlyphAtIndex: is remmed because I get the same error with or without it. The error is: !!! _NSLayoutTreeSetLocationForGlyphRange invalid glyph range {3,6}

Here's the relevant portion of the backtrace:

#0  0x90f09245 in CFGetTypeID ()
#1  0x966c2aa6 in TTypesetterRunArray::RelayoutRun ()
#2  0x966c29aa in TTypesetter::MakeLineConsistent ()
#3  0x966b0d17 in TTypesetter::FinishLineFill ()
#4  0x966ad796 in CTTypesetterCreateLine ()
#5 0x902a74a4 in -[NSATSLineFragment layoutForStartingGlyphAtIndex:characterIndex:minPosition:maxPosition:lineFragmentRect :] () #6 0x902a5f21 in -[NSATSTypesetter _layoutLineFragmentStartingWithGlyphAtIndex:characterIndex:atPoint:renderingContext :] ()
#7  0x902de87a in -[NSATSTypesetter layoutParagraphAtPoint:] ()
#8 0x90286f60 in -[NSTypesetter _layoutGlyphsInLayoutManager:startingAtGlyphIndex:maxNumberOfLineFragments:maxCharacterIndex:nextGlyphIndex:nextCharacterIndex :] () #9 0x90873d3a in -[NSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments :] () #10 0x905ef494 in -[NSATSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments :] () #11 0x902dc416 in -[NSLayoutManager(NSPrivate) _fillLayoutHoleForCharacterRange:desiredNumberOfLines:isSoft:] ()
#12 0x9041b646 in _NSFastFillAllLayoutHolesForGlyphRange ()
#13 0x902e9fea in -[NSLayoutManager textContainerForGlyphAtIndex:effectiveRange:] ()
#14 0x903cdf11 in -[NSTextView(NSSharing) didChangeText] ()
#15 0x0004b1ff in -[RCTextManager replaceSelectionWithAttributedString:replaceAllAttributes:undoActionName:filterAttributes :] (self=0x10d4da0, _cmd=0x7496c, attrString=0x12641d0, replaceAllAttrs=0 '\000', actionName=0x84f70, flag=0 '\000') at /Users/ ross/Documents/MyApp/Controller Classes/RCTextManager.m:287 #16 0x0005b9a6 in -[Document(Document_Menus) menuInsertPageNumber:] (self=0x104e510, _cmd=0x117a40, sender=0x1014730) at /Users/ross/ Documents/MyApp/Controller Classes/Document_Menus.m:143

Should I take a different approach?


On Mar 17, 2008, at 1:00 PM, Douglas Davidson wrote:


On Mar 17, 2008, at 9:31 AM, Ross Carter wrote:


What is the correct approach to take when you need NSLayoutManager to make on-the-fly adjustments to the glyphs

Usually the glyph generator is used for default glyph generation, and the typesetter is used to make adjustments to the glyphs that may depend on layout--since it is the typesetter that is actually doing the layout.

However, that's not your problem here, since it looks like you haven't yet gotten to the generation of the dynamic text; you're just using a static array of newGlyphs. The problem is that you need to inform the layout manager of the character index <-> glyph index mapping. When you use the bulk insertion method insertGlyphs:length:forStartingGlyphAtIndex:characterIndex:, you get the default behavior of a one-to-one character->glyph mapping for those glyphs. If the mapping you want isn't one-to-one, that won't be correct.

What you don't say is what the number of characters is that you want to have mapped to these three glyphs. You're using the effective range of an attribute, which is a somewhat dangerous practice--the effective range in general is only guaranteed to be some range over which the attribute applies, which may or may not be what you want. What happens if you have two distinct occurrences of this attribute, say two occurrences of your page number marker, that happen to lie adjacent to each other?

A simple case would be if the range of characters you're mapping happens to be of length 1. In that case, I believe you should just be able to insert the glyphs one by one, specifying in each case the correct glyph index and the same character index, and that should take care of the mapping. If the character range is not 1, there are a number of cases and it gets more complicated.

For some general information on the mapping, take a look at the AppKit release notes for Leopard. I included some discussion there of the constraints on the mapping between characters and glyphs. Probably we should have more discussion in the documentation of exactly how one goes about setting this mapping from within the glyph generator or typesetter.

Douglas Davidson



_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to