simple sencillo que poner justificado interlineado espaciado equivale definicion como objective-c macos nstextview core-text nslayoutmanager

objective-c - sencillo - que es el espaciado en word



¿Cómo funciona el interlineado en Core Text?(¿Y por qué es diferente de NSLayoutManager?) (3)

¿Ha mirado para ver cuál es el signo del valor devuelto por CTFontGetDescent() ? Un error común es asumir que los valores de descenso son positivos, cuando en realidad tienden a ser negativos (para reflejar el hecho de que son un descenso por debajo de la línea de base de la fuente).

Como resultado, el espaciado de líneas probablemente debería establecerse en

ascent - descent + leading

Estoy tratando de dibujar texto usando las funciones Core Text, con un interlineado lo más cercano posible a lo que sería si usara NSTextView.

Toma esta fuente como ejemplo:

NSFont *font = [NSFont fontWithName:@"Times New Roman" size:96.0];

La altura de línea de esta fuente, si la usaría en un NSTextView es 111.0.

NSLayoutManager *lm = [[NSLayoutManager alloc] init]; NSLog(@"%f", [lm defaultLineHeightForFont:font]); // this is 111.0

Ahora, si hago lo mismo con Core Text, el resultado es 110.4 (asumiendo que puedes calcular la altura de la línea agregando el ascenso, el descenso y el inicio).

CTFontRef cFont = CTFontCreateWithName(CFSTR("Times New Roman"), 96.0, NULL); NSLog(@"%f", CTFontGetDescent(cFont) + CTFontGetAscent(cFont) + CTFontGetLeading(cFont)); // this is 110.390625

Esto es muy cercano a 111.0, pero para algunas fuentes la diferencia es mucho mayor. Por ejemplo, para Helvetica, NSLayoutManager da 115.0 mientras que CTFont ascenso + descenso + líder = 96.0. Claramente, para Helvetica, no podría usar ascenso + descenso + para calcular el espaciado entre líneas.

Así que pensé que usaría CTFrame y CTFramesetter para diseñar unas pocas líneas y obtener el espacio entre ellas. Pero eso también da diferentes valores.

CTFontRef cFont = CTFontCreateWithName(CFSTR("Times New Roman"), 96.0, NULL); NSDictionary *attrs = [NSDictionary dictionaryWithObject:(id)cFont forKey:(id)kCTFontAttributeName]; NSAttributedString *threeLines = [[NSAttributedString alloc] initWithString:@"abcdefg/nabcdefg/nabcdefg" attributes:attrs]; CTFramesetterRef threeLineFramesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)threeLines); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, CGRectMake(0.0, 0.0, 600.0, 600.0)); CTFrameRef threeLineFrame = CTFramesetterCreateFrame(threeLineFramesetter, CFRangeMake(0, 0), path, NULL); CGPoint lineOrigins[3]; CTFrameGetLineOrigins(threeLineFrame, CFRangeMake(0, 0), lineOrigins); NSLog(@"space between line 1 and 2: %f", lineOrigins[0].y - lineOrigins[1].y); // result: 119.278125 NSLog(@"space between line 2 and 3: %f", lineOrigins[1].y - lineOrigins[2].y); // result: 113.625000

Por lo tanto, el espacio entre líneas ahora es aún más diferente del 111.0 que se usó en mi NSTextView, y no todas las líneas son iguales. Parece que los saltos de línea agregan un poco de espacio adicional (aunque el valor predeterminado para paragraphSpacingBefore es 0.0).

Estoy resolviendo este problema ahora al obtener la altura de la línea a través de NSLayoutManager y luego dibujar individualmente cada CTLine, pero me pregunto si hay una mejor manera de hacerlo.


OK, así que eché un vistazo a lo que sucede en las entrañas de NSLayoutManager, y parece, según mi lectura del desmontaje, que el código que utiliza se reduce a algo como esto:

CGFloat ascent = CTFontGetAscent(theFont); CGFloat descent = CTFontGetDescent(theFont); CGFloat leading = CTFontGetLeading(theFont); if (leading < 0) leading = 0; leading = floor (leading + 0.5); lineHeight = floor (ascent + 0.5) + floor (descent + 0.5) + leading; if (leading > 0) ascenderDelta = 0; else ascenderDelta = floor (0.2 * lineHeight + 0.5); defaultLineHeight = lineHeight + ascenderDelta;

Esto le dará los valores 111.0 y 115.0 para las dos fuentes que mencionó anteriormente.

Debo agregar que la forma correcta, de acuerdo con la especificación de OpenType, es simplemente agregar los tres valores (teniendo cuidado, si está utilizando una API que no los hace todos positivos, para obtener el signo del valor de descenso correcto ).


sencillo. configure una cadena y un marco de prueba y compare el origen de dos líneas de la fuente que desea. Luego, si desea calcular el avance, solo use el descenso de acento de la altura de la línea para realizar el cálculo.

- (float)getLineHeight { CFMutableAttributedStringRef testAttrString; testAttrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); NSString *testString = @"testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; CFAttributedStringReplaceString (testAttrString, CFRangeMake(0, 0), (CFStringRef)testString); CTFontRef myFont1 = CTFontCreateWithName((CFStringRef)@"Helvetica", 30, NULL); CFRange range = CFRangeMake(0,testString.length); CFAttributedStringSetAttribute(testAttrString, range, kCTFontAttributeName, myFont1); CGMutablePathRef path = CGPathCreateMutable(); CGRect bounds; if ([model isLandscape]) { bounds = CGRectMake(0, 10, 1024-20, 768); } else { bounds = CGRectMake(0, 10, 768-20, 1024); } CGPathAddRect(path, NULL, bounds); CTFramesetterRef testFramesetter = CTFramesetterCreateWithAttributedString(testAttrString); CTFrameRef testFrameRef = CTFramesetterCreateFrame(testFramesetter,CFRangeMake(0, 0), path, NULL); CGPoint origins1,origins2; CTFrameGetLineOrigins(testFrameRef, CFRangeMake(0, 1), &origins1); CTFrameGetLineOrigins(testFrameRef, CFRangeMake(1, 1), &origins2); return origins1.y-origins2.y; }