How does NSButtonCell do its drawing?

  • Hi guys,

      does anybody know how to draw international and English text in
    Cocoa and have it correctly centered vertically? I tried
    NSLayoutManager/NSTextStorage/NSTextContainer, but the measurements I
    get from -usedRectForTextContainer: always include a highly inflated
    descent for Japanese text, which means my text is off towards the top
    as soon as it contains a Japanese word.

      I presume it's the descent, because when I tried to use ATSUI for
    text measuring it gave me the same odd measurements. The descent is
    almost as high as the text itself, while for Latin-only text it's
    about a quarter. Which is funny, considering Japanese doesn't have any
    descenders as far as I know.

      It seems that NSButtonCell correctly does its measuring, because
    there Japanese text is properly adjusted. Does anyone know what clever
    thing NSButtonCell might be doing so it can properly center text
    vertically?

      Any AppKit engineer happen to hang out here that could have a peek
    at the sources, perhaps?

    Cheers,
    -- M. Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
  • On Jan 21, 2008, at 12:15 PM, Uli Kusterer wrote:

    > does anybody know how to draw international and English text in
    > Cocoa and have it correctly centered vertically? I tried
    > NSLayoutManager/NSTextStorage/NSTextContainer, but the measurements
    > I get from -usedRectForTextContainer: always include a highly
    > inflated descent for Japanese text, which means my text is off
    > towards the top as soon as it contains a Japanese word.
    >
    > I presume it's the descent, because when I tried to use ATSUI for
    > text measuring it gave me the same odd measurements. The descent is
    > almost as high as the text itself, while for Latin-only text it's
    > about a quarter. Which is funny, considering Japanese doesn't have
    > any descenders as far as I know.
    >
    > It seems that NSButtonCell correctly does its measuring, because
    > there Japanese text is properly adjusted. Does anyone know what
    > clever thing NSButtonCell might be doing so it can properly center
    > text vertically?

    For me, it was as simple as using NSAttributedString instances,
    sending a "size" message to get the NSSize and then exactly centering
    a box of that width/height within my cell's bounds.  Then, I just use
    drawInRect: to do the rendering.

    Using the baseline approach (computing, e.g. the "x" height, or
    working with ascenders/descenders) always yielding incorrect results
    (at least in my eyes).  Probably similar "wrong" results as you're
    seeing.

    I've been using this approach extensively with Unicode text (past 3
    years).  I always use systemFontOfSize: which ultimately renders in
    the appropriate font for a particular language.  I've used many
    different sizes ranging from 18 pt to 48 pt.  Since my baseline is
    Tiger, I've only though looked at this on 10.4 and later.  I would
    hope 10.3 and even earlier would produce similar, if not identical
    output.

    Output also looked good no matter what the res-ind scaling factor (0.5
    through 3.0 to include non-integral ones like 1.33333).

    I've also specifically looked at Japanese (Kanji, Hiragana and
    Katakana characters) and haven't noticed anything strange.

    I do also modify my centering algorithm if the text includes a
    shadow.  Because of what I'll call existing bugs, the size of an
    attributed string doesn't always take into account it's shadow
    metrics.  I thus modify the height and width of the bounds returned by
    size by a factor of the sum of shadow blur and shadow offset in a
    particular direction.

    BTW, When I did this work (end of 2004), I did quite a bit of
    Photoshop comparisons of Aqua output and my own.  The approach I'm
    using seems to be right on.

    Finally, here are some screen shots from my app you can use to
    determine if my centering logic would appeal to you:

    <http://www.instantinteractive.com/drills_slides.html>

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • On 21.01.2008, at 20:15, Ricky Sharp wrote:
    > For me, it was as simple as using NSAttributedString instances,
    > sending a "size" message to get the NSSize and then exactly
    > centering a box of that width/height within my cell's bounds.  Then,
    > I just use drawInRect: to do the rendering.

      Thanks for the answer, sorry for the delay.

      I'm doing something similar for one-line text. The trouble is, I'd
    like to be able to also correctly measure wrapped multi-line text. I
    was hoping that there was a way to just create and cache the text
    system objects and measure those. The -size method is good for un-
    wrapped text, but of course creates and tears down its text system
    objects every time, which I was hoping to be able to avoid, as this
    text is going to be moved around a lot.

      My thinking was that if I knew how the button cell makes its
    Japanese text center correctly, I might be able to do the same for
    Japanese.

    > Using the baseline approach (computing, e.g. the "x" height, or
    > working with ascenders/descenders) always yielding incorrect results
    > (at least in my eyes).  Probably similar "wrong" results as you're
    > seeing.

      Yes, that's what surprises me. I thought about not accounting for
    the descent for Japanese, but that's no solution with English words
    (e.g. my app's name) interspersed with the Japanese stuff, not to
    mention there seems to be no way (except looking at each character
    directly and comparing its Unicode range to a table) to detect if a
    string contains Japanese glyphs.

    > I've been using this approach extensively with Unicode text (past 3
    > years).  I always use systemFontOfSize: which ultimately renders in
    > the appropriate font for a particular language.  I've used many
    > different sizes ranging from 18 pt to 48 pt.  Since my baseline is
    > Tiger, I've only though looked at this on 10.4 and later.  I would
    > hope 10.3 and even earlier would produce similar, if not identical
    > output.

      I only need 10.4 and up at the moment, so that's not a thing I'd
    have to worry about. Though I do want to use some custom fonts, where
    available and applicable (i.e. at least in Latin text). I'm trying to
    size my text to exactly fit a button, and things just look horribly
    off as soon as there's a word of Japanese text.

    > Output also looked good no matter what the res-ind scaling factor
    > (0.5 through 3.0 to include non-integral ones like 1.33333).
    >
    > I've also specifically looked at Japanese (Kanji, Hiragana and
    > Katakana characters) and haven't noticed anything strange.
    >
    > (...)
    >
    > Finally, here are some screen shots from my app you can use to
    > determine if my centering logic would appeal to you:
    >
    > <http://www.instantinteractive.com/drills_slides.html>

      It doesn't seem to be as bad at larger text sizes (which is what
    you're using), but in many cases I'm working with the small system
    font. I even tried scaling the CGContext down and drawing at larger
    sizes, hoping that text measurement might be more accurate, but that
    didn't help. I'm really curious why NSButtonCell can do it right, and
    for me it's this iffy. Does it turn off the leading or something?

    Cheers,
    -- M. Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
    http://www.zathras.de
  • On Friday, February 01, 2008, at 05:01AM, "Uli Kusterer" <witness.of.teachtext...> wrote:
    > On 21.01.2008, at 20:15, Ricky Sharp wrote:
    >> For me, it was as simple as using NSAttributedString instances,
    >> sending a "size" message to get the NSSize and then exactly
    >> centering a box of that width/height within my cell's bounds.  Then,
    >> I just use drawInRect: to do the rendering.
    >
    > Thanks for the answer, sorry for the delay.
    >
    > I'm doing something similar for one-line text. The trouble is, I'd
    > like to be able to also correctly measure wrapped multi-line text. I
    > was hoping that there was a way to just create and cache the text
    > system objects and measure those. The -size method is good for un-
    > wrapped text, but of course creates and tears down its text system
    > objects every time, which I was hoping to be able to avoid, as this
    > text is going to be moved around a lot.

    Ahh, I should have also posted what I do for multi-line rendering too.  I'll write in when I get home this afternoon.  Anyhow, my text rendering allows me to do any of the three text alignments along with either single-line or multi-line text.

    > It doesn't seem to be as bad at larger text sizes (which is what
    > you're using), but in many cases I'm working with the small system
    > font. I even tried scaling the CGContext down and drawing at larger
    > sizes, hoping that text measurement might be more accurate, but that
    > didn't help. I'm really curious why NSButtonCell can do it right, and
    > for me it's this iffy. Does it turn off the leading or something?

    Not sure.  I'll check this out tonight.  My UI allows me to use images of any size, so I can easily create a test nib and make buttons the same size as the various Aqua buttons.  Then, I'll fill them with single and multi-line text with point sizes say 12 or less.  I'll then compare the output in Photoshop to see if I can guess at what metrics are used in rendering.

    --
    Rick
  • On Feb 1, 2008, at 6:33 AM, RICKY SHARP wrote:

    >
    > On Friday, February 01, 2008, at 05:01AM, "Uli Kusterer" <witness.of.teachtext...>
    >> wrote:
    >> On 21.01.2008, at 20:15, Ricky Sharp wrote:
    >>> For me, it was as simple as using NSAttributedString instances,
    >>> sending a "size" message to get the NSSize and then exactly
    >>> centering a box of that width/height within my cell's bounds.  Then,
    >>> I just use drawInRect: to do the rendering.
    >>
    >> Thanks for the answer, sorry for the delay.
    >>
    >> I'm doing something similar for one-line text. The trouble is, I'd
    >> like to be able to also correctly measure wrapped multi-line text. I
    >> was hoping that there was a way to just create and cache the text
    >> system objects and measure those. The -size method is good for un-
    >> wrapped text, but of course creates and tears down its text system
    >> objects every time, which I was hoping to be able to avoid, as this
    >> text is going to be moved around a lot.
    >
    > Ahh, I should have also posted what I do for multi-line rendering
    > too.  I'll write in when I get home this afternoon.  Anyhow, my text
    > rendering allows me to do any of the three text alignments along
    > with either single-line or multi-line text.

    Actually, for my button text rendering, it's the same process; I still
    use NSAttributedString's size method.  Whenever I need multi-line text
    on a button, I just enter in a "soft" return in the button's title
    (i.e. option-Return within IB).

    I do have a "static text" widget which does render things differently
    for multi-line.  Although, all I do there is simply call drawInRect:.
    I don't pre-measure anything to see how things will fit and thus the
    text could be truncated.  But, for my needs, I always just sized the
    control width-wise in IB and then hit my custom palettes/plug-in's
    'size to fit' button which then took care of setting an appropriate
    vertical height.  For this particular control, I never do any vertical
    alignment.

    So, my approach, unfortunately doesn't give you something for wrapped
    text such that the text doesn't contain arbitrary line breaks.

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
previous month january 2008 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 31      
Go to today