调用CTRunDelegate回调时如何获取像素坐标

辛尼沙

我已将动态文本绘制到自定义UIImageView中。文本可以包含:-)或;-)之类的字符组合,我想用PNG图像替换。

对于下面的一堆代码,我深表歉意。

创建CTRunDelegate的代码如下:

CTRunDelegateCallbacks callbacks;
callbacks.version = kCTRunDelegateVersion1;
callbacks.dealloc = emoticonDeallocationCallback;
callbacks.getAscent = emoticonGetAscentCallback;
callbacks.getDescent = emoticonGetDescentCallback;
callbacks.getWidth = emoticonGetWidthCallback;
// Functions: emoticonDeallocationCallback, emoticonGetAscentCallback, emoticonGetDescentCallback, emoticonGetWidthCallback are properly defined callback functions

CTRunDelegateRef ctrun_delegate = CTRunDelegateCreate(&callbacks, self);
// self is what delegate will be using as void*refCon parameter

用于创建属性字符串的代码是:

NSMutableAttributedString* attString = [[NSMutableAttributedString alloc] initWithString:self.data attributes:attrs];
// self.data is string containing text
// attrs is just setting for font type and color

然后,我将CTRunDelegate添加到此字符串:

CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attString, range, kCTRunDelegateAttributeName, ctrun_delegate);
// where range is for one single emoticon location in text (eg. location=5, length = 2)
// ctrun_delegate is previously created delegate for certain type of emoticon

回调函数的定义如下:

void emoticonDeallocationCallback(void*refCon)
{
    // dealloc code goes here
}
CGFloat emoticonGetAscentCallback(void * refCon)
{
    return 10.0;
}
CGFloat emoticonGetDescentCallback(void * refCon)
{
    return 4.0;
}
CGFloat emoticonGetWidthCallback(void * refCon)
{
    return 30.0;
}

现在所有这些工作正常-我调用了回调函数,并且可以看到宽度,上升和下降影响检测到的“表情符号字符组合”之前和之后文本的绘制方式。

现在,我想在产生“孔”的位置绘制图像,但是我找不到任何文档可以指导我如何在每个回调中获取像素(或其他坐标)坐标。

谁能指导我阅读这些内容?

提前致谢!

聚苯乙烯

据我所知,在调用CTFramesetterCreateWithAttributedString时会调用回调。因此,基本上没有图纸在进行中。我找不到任何显示表情符号位置与绘制文本中的位置匹配的示例。能做到吗

辛尼沙

我找到了解决方案!

回顾一下:问题是使用CoreText将文本绘制到UIImageView中,除了明显的字体类型和颜色格式外,此文本还需要用小图像替换文本的一部分,并在替换子文本所在的位置插入(例如:- )将变成笑脸)。

这是如何做:

1)在提供的字符串中搜索所有受支持的表情符号(例如,搜索:-)子字符串)

NSRange found = [self.rawtext rangeOfString:emoticonString options:NSCaseInsensitiveSearch range:searchRange];

如果发现出现的情况,请将其存储在CFRange中:

CFRange cf_found = CFRangeMake(found.location, found.length);

如果要搜索多个不同的图释(例如::) :-) ;-);)等),请按其位置的升序对所有找到的出现进行排序。

2)替换所有要用图像替换的图释子字符串(例如:-)。此后,您还必须更新找到的位置以匹配这些新空间。它并不像听起来那么复杂。

3)对每个表情使用CTRunDelegateCreate将回调添加到新创建的字符串(不带:-的字符串,而是[SPACE])。

4)回调函数显然应该根据您将使用的图像大小返回正确的图释宽度。

5)一旦您将执行CTFramesetterCreateWithAttributedString,这些回调函数也将被执行,从而提供框架设定器数据,这些数据稍后将用于创建用于在给定框架路径中绘制的字形。

6)现在是我想念的部分:一旦使用CTFramesetterCreateFrame为照排机创建框架,请遍历所有找到的图释并执行以下操作:

从帧中获取行数,并获取第一行的原点:

CFArrayRef lines = CTFrameGetLines(frame);
int linenum = CFArrayGetCount(lines);

CGPoint origins[linenum];
CTFrameGetLineOrigins(frame, CFRangeMake(0, linenum), origins);

在每个表情符号的所有行中循环,寻找包含该表情符号的字形(基于每个表情符号的range.location和每个字形中的字符数):

(灵感来自这里:CTRunGetImageBounds返回不正确的结果

int eloc = emoticon.range.location; // emoticon's location in text

for( int i = 0; i<linenum; i++ )
{
    CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, i);
    CFArrayRef gruns = CTLineGetGlyphRuns(line);
    int grunnum = CFArrayGetCount(gruns);
    for( int j = 0; j<grunnum; j++ )
    {
        CTRunRef grun = (CTRunRef) CFArrayGetValueAtIndex(gruns, j);

        int glyphnum = CTRunGetGlyphCount(grun);
        if( eloc > glyphnum )
        {
            eloc -= glyphnum;
        }
        else
        {
            CFRange runRange = CTRunGetStringRange(grun);
            CGRect runBounds;
            CGFloat ascent,descent;
            runBounds.size.width = CTRunGetTypographicBounds(grun, CFRangeMake(0, 0), &ascent, &descent, NULL);
            runBounds.size.height = ascent + descent;

            CGFloat xOffset = CTLineGetOffsetForStringIndex(line, runRange.location, NULL);

            runBounds.origin.x = origins[i].x + xOffset;
            runBounds.origin.y = origins[i].y;
            runBounds.origin.y -= descent;

            emoticon.location = CGPointMake(runBounds.origin.x + runBounds.size.width, runBounds.origin.y);

            emoticon.size = CGPointMake([emoticon EmoticonWidth] ,runBounds.size.height);

            break;
        }
    }
}

请不要将此代码当作复制粘贴和将要使用的代码,因为我不得不剥离许多其他内容-因此,这只是为了说明我所做的事情,而不是让您按原样使用它。

7)最后,我可以创建上下文并在正确的位置绘制文本和表情符号:

if(currentContext)
{
    CGContextSaveGState(currentContext);
    {
        CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
        CTFrameDraw(frame, currentContext);
    }
    CGContextRestoreGState(currentContext);

    if( foundEmoticons != nil )
    {
        for( FoundEmoticon *emoticon in foundEmoticons )
        {
            [emoticon DrawInContext:currentContext];
        }
    }
}

和绘制图释的功能(我只是绘制它的边界和枢轴点):

-(void) DrawInContext:(CGContext*)currentContext
{
    CGFloat R = round(10.0 * [self randomFloat] ) * 0.1;
    CGFloat G = round(10.0 * [self randomFloat] ) * 0.1;
    CGFloat B = round(10.0 * [self randomFloat] ) * 0.1;
    CGContextSetRGBStrokeColor(currentContext,R,G,B,1.0);

    CGFloat pivotSize = 8.0;

    CGContextBeginPath(currentContext);
    CGContextMoveToPoint(currentContext, self.location.x, self.location.y - pivotSize);
    CGContextAddLineToPoint(currentContext, self.location.x, self.location.y + pivotSize);
    CGContextMoveToPoint(currentContext, self.location.x - pivotSize, self.location.y);
    CGContextAddLineToPoint(currentContext, self.location.x + pivotSize, self.location.y);
    CGContextDrawPath(currentContext, kCGPathStroke);

    CGContextBeginPath(currentContext);
    CGContextMoveToPoint(currentContext, self.location.x, self.location.y);
    CGContextAddLineToPoint(currentContext, self.location.x + self.size.x, self.location.y);
    CGContextAddLineToPoint(currentContext, self.location.x + self.size.x, self.location.y + self.size.y);
    CGContextAddLineToPoint(currentContext, self.location.x, self.location.y + self.size.y);
    CGContextAddLineToPoint(currentContext, self.location.x, self.location.y);
    CGContextDrawPath(currentContext, kCGPathStroke);
}

结果图像:http : //i57.tinypic.com/rigis5.png

:-)))

聚苯乙烯

这是带有多行的结果图像:http : //i61.tinypic.com/2pyce83.png

聚苯硫醚

这是具有多行和带有表情符号的PNG图像的结果图像:http : //i61.tinypic.com/23ixr1y.png

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在单击时获取传单ImageOverlay的像素坐标

如何在获取调用函数返回的内容时将局部变量传递给回调?

回调如何调用?

如何通过低级鼠标回调方法从lParam获取鼠标光标坐标?

获取 QImage 像素坐标

JNI如何调用Java和回调本机,以及如何获取JVM std io

如何设置像素坐标?

如何从Java调用Nashorns`ScriptFunction`回调?

如何在回调中调用 useDispatch

如何使用Mockk调用Lambda回调

如何调用Swift闭包回调

如何使用matplotlib imread获取所有蓝色像素的坐标?

如何从OpenCV Python中的功能匹配获取像素坐标

如何从图像中获取像素的x,y坐标颜色?

如何从画布多边形(填充区域)获取像素坐标

如何从鼠标坐标中获取像素的正确位置?

如何获取图像中像素的x,y(坐标)数组

GIS / GEOTiff / GDAL / Python如何从像素获取坐标

如何获取Kinect中像素深度的X,Y坐标?

在emgu 3.0.0.0中纠正形状后如何获取像素的坐标?

如何在Java中的回调中获取调用者对象?

Kotlin:如何使用 Volly 调用 API 并通过回调方法同步获取响应

单击时如何显示图片并打印像素坐标?

当子节点调用主节点的回调时,如何更新子节点?

从排序方法调用参数时,如何将参数传递给回调

在调用使用方法之后分配回调时如何执行

单击Card Action时如何调用特定的回调-Bot Framework

调用onDestroy回调时如何保存计数器的值?

获取白色像素的坐标(OpenCV)