使用核心图形绘制文本

9

我需要将文本居中绘制到CGContext中。

我开始尝试使用Cocoa的方法。我创建了一个带有文本的NSCell,并尝试这样绘制它:

NSGraphicsContext* newCtx = [NSGraphicsContext
     graphicsContextWithGraphicsPort:bitmapContext flipped:true];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:newCtx];
[pCell setFont:font];
[pCell drawWithFrame:rect inView:nil];
[NSGraphicsContext restoreGraphicsState];

但是CGBitmapContext似乎没有呈现文本内容。可能是因为我必须为inView:参数传递nil。

所以我尝试切换到使用Core Graphics进行文本渲染:

最简单的方法似乎是使用CGContextSelectFont通过后缀名称和点大小选择字体,但是CGContextShowTextAtPoint仅接受非Unicode字符,并且似乎没有明显的方法来将文本适合矩形或计算文本行的范围以手动布置矩形。

然后,可以创建一个CGFont,并通过CGContextSetFont进行设置。绘制此文本需要使用CGContextShowGlyphsAtPoint,但是再次CGContext似乎缺少计算生成文本的边界矩形或将文本包装到矩形的功能。而且如何将字符串转换为CGGlyphs数组并不明显。

下一个选项是尝试使用CoreText来呈现字符串。但是Core Text类非常复杂,虽然有示例显示如何在指定字体、矩形中显示文本,但没有示例演示如何计算CoreText字符串的边界矩形。

所以:

  • 给定CGFont和CGContext,如何计算一些文本的边界矩形,以及如何将文本字符串转换为CGGlyphs数组?
  • 给定字符串、CGContext和后缀名称和点大小,我需要创建哪些Core Text对象来计算字符串的边界矩形和/或在CGContext上将字符串包装到矩形中。
  • 给定字符串和NSFont-如何将字符串呈现到CGBitmapContext?我已经知道如何获取其范围。

你能画一个期望结果的示例吗? - hfossli
也许有点简单,但我想要实现的结果是在一个矩形中绘制一个字符串,使用指定字体(字体和大小)。该字符串必须自动换行,并且我需要能够选择左对齐、居中对齐或右对齐的行,以及段落的顶部、中心或底部对齐方式。 - Chris Becke
4个回答

9

经过四天的搜索,我终于找到了答案。我真希望苹果能够制作更好的技术文档。下面是内容:

我假设你已经拥有了 CGFontRef 对象。如果没有,请告诉我,我会告诉你如何将 TTF 文件从资源包加载到 CgFontRef 中。

以下是计算任何字符串在任何 CGFontRef 下边界的代码片段:

        int charCount = [string length];
        CGGlyph glyphs[charCount];
        CGRect rects[charCount];

        CTFontGetGlyphsForCharacters(theCTFont, (const unichar*)[string cStringUsingEncoding:NSUnicodeStringEncoding], glyphs, charCount);
        CTFontGetBoundingRectsForGlyphs(theCTFont, kCTFontDefaultOrientation, glyphs, rects, charCount);

        int totalwidth = 0, maxheight = 0;
        for (int i=0; i < charCount; i++)
        {
            totalwidth += rects[i].size.width;
            maxheight = maxheight < rects[i].size.height ? rects[i].size.height : maxheight;
        }

        dim = CGSizeMake(totalwidth, maxheight);

使用相同的函数CTFontGetGlyphsForCharacters来获取字形。要从CGFontRef获取CTFontRef,请使用CTFontCreateWithGraphicsFont()函数。

还要记住,NSFont和CGFontRef是免费桥接的,这意味着它们可以互相转换,并且可以无缝地工作,而不需要任何额外的工作。


但是您遗漏了实际的答案...如何在矩形中实际绘制该文本...您问题中的代码使用了AppKit(NS)API,而您的问题(以及我的要求...)是使用仅CoreGraphics API在CGBitmapContext上绘制文本(因此可以跨iOS / MacOS)。您能稍微扩展一下吗? - Motti Shneor

8
我建议您继续使用上述方法,但改用NSAttributedString。
NSGraphicsContext* newCtx = [NSGraphicsContext graphicsContextWithGraphicsPort:bitmapContext flipped:true];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:newCtx];
NSAttributedString *string = /* make a string with all of the desired attributes */;
[string drawInRect:locationToDraw];
[NSGraphicsContext restoreGraphicsState];

NSGraphicsContext和对齐的API如何与CGContext及其自身的API兼容?它们能混合使用吗?我的代码是跨平台SDK的一部分,应该在iOS、MacOS等平台上运行,因此我将自己限制在CoreGraphics API范围内 - 请问您能否改进您的答案以便在CGBitmapContext上使用? - Motti Shneor

1

Swift 5 版本!

let targetSize: CGSize = // the space you have available for drawing the text
let origin: CGPoint = // where you want to position the top-left corner
let string: String = // your string
let font: UIFont = // your font

let attrs: [NSAttributedString.Key:Any] = [.font: font]
let boundingRect = string.boundingRect(with: targetSize, options: [.usesLineFragmentOrigin], attributes: attrs, context: nil)
let textRect = CGRect(origin: origin, size: boundingRect.size)
text.draw(with: textRect, options: [.usesLineFragmentOrigin], attributes: attrs, context: nil)

0
一份完整的示例代码(Objective-C):
//  Need to create pdf Graphics context for Drawing text
    CGContextRef pdfContextRef = NULL;
    CFURLRef writeFileUrl = (CFURLRef)[NSURL fileURLWithPath:writeFilePath];
    
    if(writeFileUrl != NULL){
        pdfContextRef = CGPDFContextCreateWithURL(writeFileUrl, NULL, NULL);
    }
    
//  Start page in PDF context
    CGPDFContextBeginPage(pdfContextRef, NULL);
    
    NSGraphicsContext* pdfGraphicsContext = [NSGraphicsContext graphicsContextWithCGContext:pdfContextRef flipped:false];
    [NSGraphicsContext saveGraphicsState];
    
//  Need to set the current graphics context
    [NSGraphicsContext setCurrentContext:pdfGraphicsContext];
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:@"Helvetica" size:26], NSFontAttributeName,[NSColor blackColor], NSForegroundColorAttributeName, nil];
    NSAttributedString * currentText=[[NSAttributedString alloc] initWithString:@"Write Something" attributes: attributes];

//  [currentText drawInRect: CGRectMake(0, 300, 500, 100 )];
    [currentText drawAtPoint:NSMakePoint(100, 100)];
    
    [NSGraphicsContext restoreGraphicsState];

//  Close all the created pdf Contexts
    CGPDFContextEndPage(pdfContextRef);
    CGPDFContextClose(pdfContextRef);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接