8

我正在尝试使用各种技术在 MacOS X/iOS 中获取字符的边界框。我现在展示我所有的尝试。到目前为止,如果我想获得诸如“Ä”之类的变音符号的大小,代码将失败。

使用CTFontGetBoundingRectsForGlyphs

-(void) resizeLayer1:(CATextLayer *)l toString:(NSString *)string
{
    // need to set CGFont explicitly to convert font property to a CGFontRef
    CGFontRef layerFont = CGFontCreateWithFontName((CFStringRef)@"Helvetica");
    l.font = layerFont;

    string = l.string;
    NSUInteger len = [string length];

    // get characters from NSString
    UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len);
    CFStringGetCharacters((__bridge CFStringRef)l.string, CFRangeMake(0, [l.string length]), characters);

    // Get CTFontRef from CGFontRef
    CTFontRef coreTextFont = CTFontCreateWithGraphicsFont(layerFont, l.fontSize, NULL, NULL);

    // allocate glyphs and bounding box arrays for holding the result
    // assuming that each character is only one glyph, which is wrong
    CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph)*len);
    CTFontGetGlyphsForCharacters(coreTextFont, characters, glyphs, len);

    // get bounding boxes for glyphs
    CGRect *bb = (CGRect *)malloc(sizeof(CGRect)*len);
    CTFontGetBoundingRectsForGlyphs(coreTextFont, kCTFontDefaultOrientation, glyphs, bb, len);
    CFRelease(coreTextFont);

    l.position = CGPointMake(200.f, 100.f);

    l.bounds = bb[0];

    l.backgroundColor = CGColorCreateGenericRGB(0.f, .5f, .9f, 1.f);

    free(characters);
    free(glyphs);
    free(bb);
}

结果它有点工作,但是当字形由 CATextLayer 呈现时,会有一些填充

使用CTFramesetterSuggestFrameSizeWithConstraints

-(void) resizeLayer2:(CATextLayer *)l toString:(NSString *)string
{
    // need to set CGFont explicitly to convert font property to a CGFontRef
    CGFontRef layerFont = CGFontCreateWithFontName((CFStringRef)@"Helvetica");
    l.font = layerFont;

    string = l.string;

    NSUInteger len = [string length];

    // get characters from NSString
    UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len);
    CFStringGetCharacters((__bridge CFStringRef)l.string, CFRangeMake(0, [l.string length]), characters);

    // Get CTFontRef from CGFontRef
    CTFontRef coreTextFont = CTFontCreateWithGraphicsFont(layerFont, l.fontSize, NULL, NULL);

    CFMutableAttributedStringRef attrStr = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
    CFAttributedStringReplaceString(attrStr, CFRangeMake(0, 0), (__bridge CFStringRef)string);
    CTTextAlignment alignment = kCTJustifiedTextAlignment;
    CTParagraphStyleSetting _settings[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment} };
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(_settings, sizeof(_settings) / sizeof(_settings[0]));
    CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle);
    CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, coreTextFont);
    CFRelease(paragraphStyle);

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrStr);
    CFRelease(attrStr);

    CFRange range;
    CGFloat maxWidth  = CGFLOAT_MAX;
    CGFloat maxHeight = 10000.f;
    CGSize constraint = CGSizeMake(maxWidth, maxHeight);

    //  checking frame sizes
    CGSize coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, len), nil, constraint, &range); 

    // allocate glyphs and bounding box arrays for holding the result
    // assuming that each character is only one glyph, which is wrong
    CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph)*len);
    CTFontGetGlyphsForCharacters(coreTextFont, characters, glyphs, len);

    // get bounding boxes for glyphs
    CGRect *bb = (CGRect *)malloc(sizeof(CGRect)*len);
    CTFontGetBoundingRectsForGlyphs(coreTextFont, kCTFontDefaultOrientation, glyphs, bb, len);
    CFRelease(coreTextFont);

    bb[0].origin = l.bounds.origin;
    coreTextSize.width = ceilf(coreTextSize.width);
    coreTextSize.height = ceilf(coreTextSize.height);
    bb[0].size = coreTextSize;


    l.position = CGPointMake(200.f, 100.f);


    // after setting the bounds the layer gets transparent
    l.bounds = bb[0];
    l.opaque = YES;
    return;    

    l.backgroundColor = CGColorCreateGenericRGB(0.f, .5f, .9f, 1.f);

    free(characters);
    free(glyphs);
    free(bb);
}

结果字符串的边界框是正确的。变音符号有问题:Ä 缺少点,因为边界框不够高。

使用CTLineGetTypographicBounds

-(void) resizeLayer3:(CATextLayer *)l toString:(NSString *)string
{
    // need to set CGFont explicitly to convert font property to a CGFontRef
    CGFontRef layerFont = CGFontCreateWithFontName((CFStringRef)@"Helvetica");
    l.font = layerFont;

    string = l.string;


    NSUInteger len = [string length];

    // get characters from NSString
    UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len);
    CFStringGetCharacters((__bridge CFStringRef)l.string, CFRangeMake(0, [l.string length]), characters);

    // Get CTFontRef from CGFontRef
    CTFontRef coreTextFont = CTFontCreateWithGraphicsFont(layerFont, l.fontSize, NULL, NULL);

    CFMutableAttributedStringRef attrStr = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
    CFAttributedStringReplaceString(attrStr, CFRangeMake(0, 0), (__bridge CFStringRef)string);
    CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, coreTextFont);

    CTLineRef line = CTLineCreateWithAttributedString(attrStr);
    CGFloat ascent;
    CGFloat descent;
    CGFloat width = CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
    CGFloat height = ascent+descent;
    CGSize coreTextSize = CGSizeMake(width,height);


    // get bounding boxes for glyphs
    CGRect *bb = (CGRect *)malloc(sizeof(CGRect)*len);
    CFRelease(coreTextFont);

    bb[0].origin = CGPointZero;
    coreTextSize.width = ceilf(coreTextSize.width);
    coreTextSize.height = ceilf(coreTextSize.height);
    bb[0].size = coreTextSize;


    l.position = CGPointMake(200.f, 100.f);


    // after setting the bounds the layer gets transparent
    l.bounds = bb[0];
    l.opaque = YES;
    return;    

    l.backgroundColor = CGColorCreateGenericRGB(0.f, .5f, .9f, 1.f);

    free(characters);
    free(bb);
}

结果字符串的边界框是正确的。变音符号有问题:Ä 缺少点,因为边界框不够高。

使用CTFontGetBoundingRectsForGlyphs

-(void) resizeLayer4:(CATextLayer *)l toString:(NSString *)string
{
    // need to set CGFont explicitly to convert font property to a CGFontRef
    CGFontRef layerFont = CGFontCreateWithFontName((CFStringRef)@"Helvetica");
    l.font = layerFont;

    string = l.string;

    NSUInteger len = [string length];

    // get characters from NSString
    UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len);
    CFStringGetCharacters((__bridge CFStringRef)l.string, CFRangeMake(0, [l.string length]), characters);

    // Get CTFontRef from CGFontRef
    CTFontRef coreTextFont = CTFontCreateWithGraphicsFont(layerFont, l.fontSize, NULL, NULL);

    // allocate glyphs and bounding box arrays for holding the result
    // assuming that each character is only one glyph, which is wrong
    CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph)*len);
    CTFontGetGlyphsForCharacters(coreTextFont, characters, glyphs, len);

    CGPathRef glyphPath = CTFontCreatePathForGlyph(coreTextFont, glyphs[1], NULL);
    CGRect rect = CGPathGetBoundingBox(glyphPath);

    // get bounding boxes for glyphs
    CGRect *bb = (CGRect *)malloc(sizeof(CGRect)*len);
    CTFontGetBoundingRectsForGlyphs(coreTextFont, kCTFontDefaultOrientation, glyphs, bb, len);
    CFRelease(coreTextFont);

    l.position = CGPointMake(200.f, 100.f);

    l.bounds = rect;

    l.backgroundColor = CGColorCreateGenericRGB(0.f, .5f, .9f, 1.f);

    free(characters);
    free(glyphs);
    free(bb);
}

结果字符串的边界框是正确的。变音符号本身就有问题,因为变音符号本身是一个字形,而字符是另一个字形,这意味着 Ä 由两个字形组成。怎么可能使用它?

还有其他我忽略并值得尝试的可能性吗?

编辑

第三种选择CTLineGetTypographicBounds似乎有效。但是,我遇到了一个不同的问题,即 a 中的第一行CATextLayer缺少其变音符号。第一行不知何故不够高。那意味着我在这里叫错了树。

4

2 回答 2

4

试试 CTLineGetImageBounds。但是请注意,这将包括延伸到基线以下的部分字形,例如,这与小写字母 ä 相关。

简要说明:圆形形状必须超出直线形状才能以相同的视觉重量出现。例如,参见小写 b 或 d,它们在基线处具有圆形和直线形状。

于 2012-05-10T06:13:12.803 回答
4

在第一个示例中,您似乎忽略了字形的边界矩形很可能具有负 y 原点这一事实。返回的矩形通常被视为y=0文本的基线。因此,您在边界矩形中设置了一个偏移量,这可能也是图层在文本中具有偏移量的原因。(没试过,但这么认为)

如果您对特定文本的边界不感兴趣,但选择包含各种文本的高度,您可能还想选择CTFontGetBoundingBox.

于 2012-05-10T06:16:31.590 回答