poner - Texto principal calcule el marco de la letra en iOS
mi iphone cambio de color la pantalla (1)
Hiciste una cantidad impresionante de trabajo en tu pregunta y estabas tan cerca por tu cuenta. El problema que estaba teniendo proviene de esta línea de código donde coloca los cuadros delimitadores para cada cuadro:
_characterFrames[ic].origin = CGPointMake(startOffset, lineOrigin.y);
El problema con esto es que estás anulando cualquier compensación que el marco ya tenía.
Si tuvieras que comentar esa línea, verías que todos los marcos se ubicaron más o menos en el mismo lugar pero también verías que no están posicionados exactamente en el mismo lugar. Algunos se colocan más hacia la izquierda o hacia la derecha y algunos más hacia arriba o hacia abajo. Esto significa que los marcos para los glifos tienen una posición propia.
La solución a su problema es tomar en cuenta la posición actual de los marcos cuando los mueve a su lugar correcto en las líneas. Puede hacerlo agregando xey por separado:
_characterFrames[ic].origin.x += startOffset;
_characterFrames[ic].origin.y += lineOrigin.y;
o al compensar el rectángulo:
_characterFrames[ic] = CGRectOffset(_characterFrames[ic],
startOffset, lineOrigin.y);
Ahora los cuadros delimitadores tendrán sus posiciones correctas:
y debería ver que funciona para algunas de las fuentes más extremas que existen
Necesito calcular el cuadro delimitador exacto para cada carácter (glifo) en NSAttributedString (Texto principal). Después de armar un código usado para resolver problemas similares (selección de texto central, etc.), el resultado es bastante bueno, pero solo algunos fotogramas (rojo) se calculan correctamente:
La mayoría de los marcos son lugares fuera de lugar, ya sea horizontal o verticalmente (en pequeños bits). ¿Cuál es la causa de eso? ¿Cómo puedo perfeccionar este código ?:
-(void)recalculate{
// get characters from NSString
NSUInteger len = [_attributedString.string length];
UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len);
CFStringGetCharacters((__bridge CFStringRef)_attributedString.string, CFRangeMake(0, [_attributedString.string length]), characters);
// 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(_font, characters, glyphs, len);
// get bounding boxes for glyphs
CTFontGetBoundingRectsForGlyphs(_font, kCTFontDefaultOrientation, glyphs, _characterFrames, len);
free(characters); free(glyphs);
// Measure how mush specec will be needed for this attributed string
// So we can find minimun frame needed
CFRange fitRange;
CGSize s = CTFramesetterSuggestFrameSizeWithConstraints(_framesetter, rangeAll, NULL, CGSizeMake(W, MAXFLOAT), &fitRange);
_frameRect = CGRectMake(0, 0, s.width, s.height);
CGPathRef framePath = CGPathCreateWithRect(_frameRect, NULL);
_ctFrame = CTFramesetterCreateFrame(_framesetter, rangeAll, framePath, NULL);
CGPathRelease(framePath);
// Get the lines in our frame
NSArray* lines = (NSArray*)CTFrameGetLines(_ctFrame);
_lineCount = [lines count];
// Allocate memory to hold line frames information:
if (_lineOrigins != NULL)free(_lineOrigins);
_lineOrigins = malloc(sizeof(CGPoint) * _lineCount);
if (_lineFrames != NULL)free(_lineFrames);
_lineFrames = malloc(sizeof(CGRect) * _lineCount);
// Get the origin point of each of the lines
CTFrameGetLineOrigins(_ctFrame, CFRangeMake(0, 0), _lineOrigins);
// Solution borrowew from (but simplified):
// https://github.com/twitter/twui/blob/master/lib/Support/CoreText%2BAdditions.m
// Loop throught the lines
for(CFIndex i = 0; i < _lineCount; ++i) {
CTLineRef line = (__bridge CTLineRef)[lines objectAtIndex:i];
CFRange lineRange = CTLineGetStringRange(line);
CFIndex lineStartIndex = lineRange.location;
CFIndex lineEndIndex = lineStartIndex + lineRange.length;
CGPoint lineOrigin = _lineOrigins[i];
CGFloat ascent, descent, leading;
CGFloat lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
// If we have more than 1 line, we want to find the real height of the line by measuring the distance between the current line and previous line. If it''s only 1 line, then we''ll guess the line''s height.
BOOL useRealHeight = i < _lineCount - 1;
CGFloat neighborLineY = i > 0 ? _lineOrigins[i - 1].y : (_lineCount - 1 > i ? _lineOrigins[i + 1].y : 0.0f);
CGFloat lineHeight = ceil(useRealHeight ? abs(neighborLineY - lineOrigin.y) : ascent + descent + leading);
_lineFrames[i].origin = lineOrigin;
_lineFrames[i].size = CGSizeMake(lineWidth, lineHeight);
for (int ic = lineStartIndex; ic < lineEndIndex; ic++) {
CGFloat startOffset = CTLineGetOffsetForStringIndex(line, ic, NULL);
_characterFrames[ic].origin = CGPointMake(startOffset, lineOrigin.y);
}
}
}
#pragma mark - Rendering Text:
-(void)renderInContext:(CGContextRef)context contextSize:(CGSize)size{
CGContextSaveGState(context);
// Draw Core Text attributes string:
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, CGRectGetHeight(_frameRect));
CGContextScaleCTM(context, 1.0, -1.0);
CTFrameDraw(_ctFrame, context);
// Draw line and letter frames:
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:0.5].CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextBeginPath(context);
CGContextAddRects(context, _lineFrames, _lineCount);
CGContextClosePath(context);
CGContextStrokePath(context);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.5].CGColor);
CGContextBeginPath(context);
CGContextAddRects(context, _characterFrames, _attributedString.string.length);
CGContextClosePath(context);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}