核心文本中的垂直文本对齐

Vertical Text Alignment in Core Text

编辑: Here's a Github Gist 下面的代码,如果它更具可读性的话。

我正在编写一种使用 Core Text 在 PDF 的矩形内绘制文本的方法。我写的东西完成了我需要的一切,除了垂直对齐(顶部、中心、底部)。目前文字的标准绘制方式是上对齐,我特别需要下对齐。

- (void)drawText:(NSString *)textToDraw inFrame:(CGRect)frameRect withFont:(UIFont *)originalFont textColor:(UIColor *)textColor alignment:(PDFTextAlignment)alignment verticalAlignment:(PDFTextVerticalAlignment)verticalAlignment {

    if (!textToDraw) {
        // If nil, give it an empty value to draw
        textToDraw = @"";
    }


    // Prepare font
    CTFontRef font = [self ctFontRefFromUIFont:originalFont];
    CGColorRef color = textColor.CGColor;

    // Paragraph
    CTTextAlignment ctAlignment;
    switch (alignment) {
        case PDFTextAlignmentLeft:
            ctAlignment = kCTTextAlignmentLeft;
            break;
        case PDFTextAlignmentCenter:
            ctAlignment = kCTTextAlignmentCenter;
            break;
        case PDFTextAlignmentRight:
            ctAlignment = kCTTextAlignmentRight;
            break;
        case PDFTextAlignmentJustified:
            ctAlignment = kCTTextAlignmentJustified;
            break;
        default:
            ctAlignment = kCTTextAlignmentLeft;
            break;
    }

    CTParagraphStyleSetting settings[] = {
    {kCTParagraphStyleSpecifierAlignment, sizeof(ctAlignment), &ctAlignment},
    };

    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));

    // Create an attributed string
    CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName, kCTParagraphStyleAttributeName };
    CFTypeRef values[] = { font, color, paragraphStyle };
    CFDictionaryRef attr = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values,
                                          sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    CFStringRef stringRef = (__bridge CFStringRef)textToDraw;
    // Prepare the text using a Core Text Framesetter.
    CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, stringRef, attr);

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);

    CGMutablePathRef framePath = CGPathCreateMutable();    

    CGPathAddRect(framePath, NULL, frameRect);

    // Get the frame that will do the rendering.
    CFRange currentRange = CFRangeMake(0, 0);
    CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
    CGPathRelease(framePath);

    // Get the graphics context.
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    // Put the text matrix into a known state. This ensures
    // that no old scaling factors are left in place.
    CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);

    // Core Text draws from the bottom-left corner up, so flip
    // the current transform prior to drawing.
    // Modify this to take into consideration the origin.
    CGContextTranslateCTM(currentContext, 0, frameRect.origin.y*2);
    CGContextScaleCTM(currentContext, 1.0, -1.0);

    // Draw the frame.
    CTFrameDraw(frameRef, currentContext);

    // Add these two lines to reverse the earlier transformation.
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    CGContextTranslateCTM(currentContext, 0, (-1)*frameRect.origin.y*2);

    CFRelease(frameRef);
    CFRelease(stringRef);
    CFRelease(framesetter);
    CFRelease(font);
    CFRelease(paragraphStyle);
}

这是当前如何在矩形中绘制文本的示例(请注意,用线条绘制的矩形与我传入的 frameRect 相同)... 。 我希望此文本在此矩形中显示为底部对齐,如下所示... .

我已经完成了 this SO post 上的解决方案,但未能正确对齐文本。

我正在传递垂直对齐的枚举值,因此理想情况下我将使用以下条件确定垂直对齐。

if (verticalAlignment == PDFTextVerticalAlignmentTop) {
    // Top align
} else if (verticalAlignment == PDFTextVerticalAlignmentCenter) {
    // Center (vertical) align
} else if (verticalAlignment == PDFTextVerticalAlignmentBottom) {
    // Bottom align
} else {
    // Default: Bottom alignment  
}

谢谢!

由于我不太确定您是如何通过链接 post 找到解决方案的,我将根据其中一个答案提出解决方案。这可能正是您尝试过的,但也可能是合适的解决方案。

我假设存在一种获取 CTFrameRef 大小的方法,只要返回适当的 CGSize,就将该实现留给您。除了链接的 SO post 中显示的方法外,它可能还值得一看 CTFramesetterSuggestFrameSizeWithConstraints。假设有这个功能,调整这部分绘图功能应该就够了

...
CGMutablePathRef framePath = CGPathCreateMutable();    

// Get the graphics context.
CGContextRef currentContext = UIGraphicsGetCurrentContext();

CGRect textRect = frameRect;  // Will also give you the default behavior for top align
CGSize textSize = [self measureFrame:frameref forContext:currentContext];

if (verticalAlignment == PDFTextVerticalAlignmentBottom) {
    // Bottom align
    textRect.size.height = textSize.height;
    textRect.origin.y = frameRect.origin.y + frameRect.size.height - textSize.height;
} else if (verticalAlignment == PDFTextVerticalAlignmentCenter) {
    // Center (vertical) align
    textRect.size.height = textSize.height;
    textRect.origin.y = frameRect.origin.y + (frameRect.size.height - textSize.height) / 2;
}

CGPathAddRect(framePath, NULL, textRect);
...