Custom text via Core Text in a layer.

  • Hi All,

    I'm using the following code to draw an attributed string into a layer
    (my own CATextLayer), but the text *always* draws black - is there
    something simple that I'm doing wrong? I am of course assuming that
    the colour into in the attributed string would determine how that
    content is being drawn.

    Please note: most code is taken directly from the core-text API docs :-)

    // HEADER

    @interface TextLayerCustom : EffectorBaseLayer
    {
    NSAttributedString* _string;
    }

    @property(readwrite, retain) NSAttributedString* string;

    @end

    // IMPL

    @implementation TextLayerCustom

    - (void) drawInContext:(CGContextRef)context
    {
    // core-text, here we come

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    CGMutablePathRef path = CGPathCreateMutable();
    if(path)
    {
      CGPathAddRect(path, NULL, self.bounds);

      CTFramesetterRef framesetter =
    CTFramesetterCreateWithAttributedString(
      (CFAttributedStringRef)_string
      );

      // Create the frame and draw it into the graphics context
      CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
      CFRangeMake(0, 0), path, NULL);

      if(frame)
      CTFrameDraw(frame, context);

      if(framesetter)
      CFRelease(framesetter);

      if(frame)
      CFRelease(frame);
    }

    if(path)
      CFRelease(path);
    }

    - (id) init
    {
    self = [super init];
    _string = 0;

    // in order to redraw so that justification and line wrapping take
    effect
    [self setNeedsDisplayOnBoundsChange:YES];

    return self;
    }

    - (void) dealloc
    {
    [_string release];
    [super dealloc];
    }

    - (NSAttributedString*) string
    {
    return [[_string retain] autorelease];
    }

    - (void) setString:(NSAttributedString *)str
    {
    [_string release];
    _string = [[NSMutableAttributedString alloc]
    initWithAttributedString:str];
    [self setNeedsDisplay];
    }

    @end

    thanks!!

    --
    John Clayton
    http://www.coderage-software.com/
  • We saw something like this when implementing CATextLayer – if your
    string has NSColor attributes, you need an NSGraphicsContext pointing
    at the current CGContext you're drawing into, e.g. something like this:

    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext:[NSGraphicsContext
    graphicsContextWithGraphicsPort:ctx flipped:NO]];

    ... draw text ...

    [NSGraphicsContext restoreGraphicsState];

    John

    On Nov 19, 2007, at 9:39 AM, John Clayton wrote:

    > Hi All,
    >
    > I'm using the following code to draw an attributed string into a
    > layer (my own CATextLayer), but the text *always* draws black - is
    > there something simple that I'm doing wrong? I am of course assuming
    > that the colour into in the attributed string would determine how
    > that content is being drawn.
    >
    > Please note: most code is taken directly from the core-text API
    > docs :-)
    >
    > // HEADER
    >
    > @interface TextLayerCustom : EffectorBaseLayer
    > {
    > NSAttributedString* _string;
    > }
    >
    > @property(readwrite, retain) NSAttributedString* string;
    >
    > @end
    >
    > // IMPL
    >
    > @implementation TextLayerCustom
    >
    > - (void) drawInContext:(CGContextRef)context
    > {
    > // core-text, here we come
    >
    > CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    >
    > CGMutablePathRef path = CGPathCreateMutable();
    > if(path)
    > {
    > CGPathAddRect(path, NULL, self.bounds);
    >
    > CTFramesetterRef framesetter =
    > CTFramesetterCreateWithAttributedString(
    > (CFAttributedStringRef)_string
    > );
    >
    > // Create the frame and draw it into the graphics context
    > CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
    > CFRangeMake(0, 0), path, NULL);
    >
    > if(frame)
    > CTFrameDraw(frame, context);
    >
    > if(framesetter)
    > CFRelease(framesetter);
    >
    > if(frame)
    > CFRelease(frame);
    > }
    >
    > if(path)
    > CFRelease(path);
    > }
    >
    > - (id) init
    > {
    > self = [super init];
    > _string = 0;
    >
    > // in order to redraw so that justification and line wrapping take
    > effect
    > [self setNeedsDisplayOnBoundsChange:YES];
    >
    > return self;
    > }
    >
    > - (void) dealloc
    > {
    > [_string release];
    > [super dealloc];
    > }
    >
    > - (NSAttributedString*) string
    > {
    > return [[_string retain] autorelease];
    > }
    >
    > - (void) setString:(NSAttributedString *)str
    > {
    > [_string release];
    > _string = [[NSMutableAttributedString alloc]
    > initWithAttributedString:str];
    > [self setNeedsDisplay];
    > }
    >
    > @end
    >
    > thanks!!
    >
    >
    > --
    > John Clayton
    > http://www.coderage-software.com/
  • I'm not - I just created a .rtf file in TextEdit, made some things
    bold, some thing yellow, gave the whole text a background shadow - and
    expected that this would then show up in core-text rendered graphics
    as well.

    Bad assumption??

    On 19/11/2007, at 6:46 PM, j o a r wrote:

    >
    > On Nov 19, 2007, at 9:39 AM, John Clayton wrote:
    >
    >> I'm using the following code to draw an attributed string into a
    >> layer (my own CATextLayer), but the text *always* draws black - is
    >> there something simple that I'm doing wrong? I am of course
    >> assuming that the colour into in the attributed string would
    >> determine how that content is being drawn.
    >
    >
    > Quick off-list question:
    >
    > How certain are you that your attributes are set up correctly? Have
    > you verified in some non-CT layer context that they do the right
    > thing?
    >
    > j o a r
    >
    >
  • > Bad assumption??
    Yes 8-).

    CoreText is designed to be the base text layout engine for out platform.
    And it's based on somewhat different graphics model from the Cocoa
    Text System.

    CT is mostly relying on the CG graphics state for specifying rendering
    attributes whereas the Text System is taking more markup attribute
    kind of approach.
    So, even though some of the text attributes are toll-free bridged,
    text attributes recognized by CF is subset of the Text System and
    possibly stay that way.

    In other words, if you need to use all text attributes supported by
    the Text System, you should stay with the Text System.

    You should have to go down to CT for very limited reasons (i.e.
    writing your own text engine).

    Aki

    On 2007/11/19, at 10:46, John Clayton wrote:

    > I'm not - I just created a .rtf file in TextEdit, made some things
    > bold, some thing yellow, gave the whole text a background shadow -
    > and expected that this would then show up in core-text rendered
    > graphics as well.
    >
    > Bad assumption??
    >
    >
    >
    > On 19/11/2007, at 6:46 PM, j o a r wrote:
    >
    >>
    >> On Nov 19, 2007, at 9:39 AM, John Clayton wrote:
    >>
    >>> I'm using the following code to draw an attributed string into a
    >>> layer (my own CATextLayer), but the text *always* draws black - is
    >>> there something simple that I'm doing wrong? I am of course
    >>> assuming that the colour into in the attributed string would
    >>> determine how that content is being drawn.
    >>
    >>
    >> Quick off-list question:
    >>
    >> How certain are you that your attributes are set up correctly? Have
    >> you verified in some non-CT layer context that they do the right
    >> thing?
    >>
    >> j o a r
    >>
    >>

  • Well,

    I'm definately not writing my own text system.  All I want to be able
    to do is stick some NSAttributedText into a bounded area, on a layer.
    Assuming CATextLayer isn't what I wanted, what would be the approach
    to adapting the code below to use the Cocoa based APi's to draw into
    that area? Is it actually easier?

    <side track / background info / reality hiatus>
    FYI: the original reason that I moved away from CATextLayer, was that
    I wanted to provide a 'duration' value for the layer - and doing that
    in conjunction with the way that I am rendering content (CARenderer)
    wasn't working.

    So I decided that I needed more control.  I've since then moved
    entirely away from touching the beginTime/duration properties of
    layers at all, and have changed my custom rendering component to use
    two newly created properties (projectBeginTime and projectDuration)
    that define exactly the same thing, but are not influenced indirectly
    by the Core Animation rendering engine (i.e. CACurrentMediaTime +
    background modifications to the presentation layer).

    Its actually more involved that that - but I thought I'd provide a wee
    bit of a sense of the tangle I'm getting myself in :-)

    Incidentally, I can (and am) using a CATextLayer derived instance for
    textual rendering in my project, which now works OK [so long as I
    don't mess with duration] - my question is driven more out of
    curiosity and learning desire.
    </side track / background info / reality hiatus>

    E.g.  this is what I've got now (which draws not-quite-the-same as the
    standard CATextLayer).  The only reason I decided to use core-text was
    because there were about a handful of lines in there to get my text
    rendered into a CGContextRef.  If Cocoa has a better way of achieving
    the same thing (i.e. attributed strings, onto a CGContextRef, for use
    by a CALayer) - then I'm all ears.

    // code begins...
    - (void) drawInContext:(CGContextRef)context
    {
    // core-text, here we come
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext:[NSGraphicsContext
    graphicsContextWithGraphicsPort:context flipped:NO]];

    CGContextSetTextMatrix(context, CGAffineTransformIdentity);

    CGMutablePathRef path = CGPathCreateMutable();
    if(path)
    {
      CGPathAddRect(path, NULL, self.bounds);

      CTFramesetterRef framesetter =
    CTFramesetterCreateWithAttributedString(
      (CFAttributedStringRef)_string
      );

      // Create the frame and draw it into the graphics context
      CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
                CFRangeMake(0, 0), path, NULL);

      if(frame)
      CTFrameDraw(frame, context);

      if(framesetter)
      CFRelease(framesetter);

      if(frame)
      CFRelease(frame);
    }

    if(path)
      CFRelease(path);

    [NSGraphicsContext restoreGraphicsState];
    }
    // code ends...

    thanks for making it this far..

    On 19/11/2007, at 8:03 PM, Aki Inoue wrote:

    >> Bad assumption??
    > Yes 8-).
    >
    > CoreText is designed to be the base text layout engine for out
    > platform.
    > And it's based on somewhat different graphics model from the Cocoa
    > Text System.
    >
    > CT is mostly relying on the CG graphics state for specifying
    > rendering attributes whereas the Text System is taking more markup
    > attribute kind of approach.
    > So, even though some of the text attributes are toll-free bridged,
    > text attributes recognized by CF is subset of the Text System and
    > possibly stay that way.
    >
    > In other words, if you need to use all text attributes supported by
    > the Text System, you should stay with the Text System.
    >
    > You should have to go down to CT for very limited reasons (i.e.
    > writing your own text engine).
    >
    > Aki
    >
    > On 2007/11/19, at 10:46, John Clayton wrote:
    >
    >> I'm not - I just created a .rtf file in TextEdit, made some things
    >> bold, some thing yellow, gave the whole text a background shadow -
    >> and expected that this would then show up in core-text rendered
    >> graphics as well.
    >>
    >> Bad assumption??
    >>
    >>
    >>
    >> On 19/11/2007, at 6:46 PM, j o a r wrote:
    >>
    >>>
    >>> On Nov 19, 2007, at 9:39 AM, John Clayton wrote:
    >>>
    >>>> I'm using the following code to draw an attributed string into a
    >>>> layer (my own CATextLayer), but the text *always* draws black -
    >>>> is there something simple that I'm doing wrong? I am of course
    >>>> assuming that the colour into in the attributed string would
    >>>> determine how that content is being drawn.
    >>>
    >>>
    >>> Quick off-list question:
    >>>
    >>> How certain are you that your attributes are set up correctly?
    >>> Have you verified in some non-CT layer context that they do the
    >>> right thing?
    >>>
    >>> j o a r
    >>>
    >>>

    >
  • John,

    The code attached below is setting up sufficient environment for Cocoa
    drawing.

    You should be able to just use NSStringDrawing methods like -
    [NSAttributedString drawWithRect:options:].

    Aki

    On 2007/11/19, at 14:29, John Clayton wrote:

    > Well,
    >
    > I'm definately not writing my own text system.  All I want to be
    > able to do is stick some NSAttributedText into a bounded area, on a
    > layer. Assuming CATextLayer isn't what I wanted, what would be the
    > approach to adapting the code below to use the Cocoa based APi's to
    > draw into that area? Is it actually easier?
    >
    > <side track / background info / reality hiatus>
    > FYI: the original reason that I moved away from CATextLayer, was
    > that I wanted to provide a 'duration' value for the layer - and
    > doing that in conjunction with the way that I am rendering content
    > (CARenderer) wasn't working.
    >
    > So I decided that I needed more control.  I've since then moved
    > entirely away from touching the beginTime/duration properties of
    > layers at all, and have changed my custom rendering component to use
    > two newly created properties (projectBeginTime and projectDuration)
    > that define exactly the same thing, but are not influenced
    > indirectly by the Core Animation rendering engine (i.e.
    > CACurrentMediaTime + background modifications to the presentation
    > layer).
    >
    > Its actually more involved that that - but I thought I'd provide a
    > wee bit of a sense of the tangle I'm getting myself in :-)
    >
    > Incidentally, I can (and am) using a CATextLayer derived instance
    > for textual rendering in my project, which now works OK [so long as
    > I don't mess with duration] - my question is driven more out of
    > curiosity and learning desire.
    > </side track / background info / reality hiatus>
    >
    > E.g.  this is what I've got now (which draws not-quite-the-same as
    > the standard CATextLayer).  The only reason I decided to use core-
    > text was because there were about a handful of lines in there to get
    > my text rendered into a CGContextRef.  If Cocoa has a better way of
    > achieving the same thing (i.e. attributed strings, onto a
    > CGContextRef, for use by a CALayer) - then I'm all ears.
    >
    > // code begins...
    > - (void) drawInContext:(CGContextRef)context
    > {
    > // core-text, here we come
    > [NSGraphicsContext saveGraphicsState];
    > [NSGraphicsContext setCurrentContext:[NSGraphicsContext
    > graphicsContextWithGraphicsPort:context flipped:NO]];
    >
    > CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    >
    > CGMutablePathRef path = CGPathCreateMutable();
    > if(path)
    > {
    > CGPathAddRect(path, NULL, self.bounds);
    >
    > CTFramesetterRef framesetter =
    > CTFramesetterCreateWithAttributedString(
    > (CFAttributedStringRef)_string
    > );
    >
    > // Create the frame and draw it into the graphics context
    > CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
    > CFRangeMake(0, 0), path, NULL);
    >
    > if(frame)
    > CTFrameDraw(frame, context);
    >
    > if(framesetter)
    > CFRelease(framesetter);
    >
    > if(frame)
    > CFRelease(frame);
    > }
    >
    > if(path)
    > CFRelease(path);
    >
    > [NSGraphicsContext restoreGraphicsState];
    > }
    > // code ends...
    >
    > thanks for making it this far..
    >
    > On 19/11/2007, at 8:03 PM, Aki Inoue wrote:
    >
    >>> Bad assumption??
    >> Yes 8-).
    >>
    >> CoreText is designed to be the base text layout engine for out
    >> platform.
    >> And it's based on somewhat different graphics model from the Cocoa
    >> Text System.
    >>
    >> CT is mostly relying on the CG graphics state for specifying
    >> rendering attributes whereas the Text System is taking more markup
    >> attribute kind of approach.
    >> So, even though some of the text attributes are toll-free bridged,
    >> text attributes recognized by CF is subset of the Text System and
    >> possibly stay that way.
    >>
    >> In other words, if you need to use all text attributes supported by
    >> the Text System, you should stay with the Text System.
    >>
    >> You should have to go down to CT for very limited reasons (i.e.
    >> writing your own text engine).
    >>
    >> Aki
    >>
    >> On 2007/11/19, at 10:46, John Clayton wrote:
    >>
    >>> I'm not - I just created a .rtf file in TextEdit, made some things
    >>> bold, some thing yellow, gave the whole text a background shadow -
    >>> and expected that this would then show up in core-text rendered
    >>> graphics as well.
    >>>
    >>> Bad assumption??
    >>>
    >>>
    >>>
    >>> On 19/11/2007, at 6:46 PM, j o a r wrote:
    >>>
    >>>>
    >>>> On Nov 19, 2007, at 9:39 AM, John Clayton wrote:
    >>>>
    >>>>> I'm using the following code to draw an attributed string into a
    >>>>> layer (my own CATextLayer), but the text *always* draws black -
    >>>>> is there something simple that I'm doing wrong? I am of course
    >>>>> assuming that the colour into in the attributed string would
    >>>>> determine how that content is being drawn.
    >>>>
    >>>>
    >>>> Quick off-list question:
    >>>>
    >>>> How certain are you that your attributes are set up correctly?
    >>>> Have you verified in some non-CT layer context that they do the
    >>>> right thing?
    >>>>
    >>>> j o a r
    >>>>
    >>>>

    >>
    >
previous month november 2007 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
Go to today