Re: Modifying glyph storage in NSLayoutManager
Re: Modifying glyph storage in NSLayoutManager
- Subject: Re: Modifying glyph storage in NSLayoutManager
- From: Ross Carter <email@hidden>
- Date: Mon, 17 Mar 2008 15:32:34 -0400
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 (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