void drawTextWithFeature(CGContextRef context, CTFontDescriptorRef fontDescriptor, CFStringRef feature, int value, CGPoint location) { CGFloat fontSize = 25; CGContextSetTextMatrix(context, CGAffineTransformScale(CGAffineTransformIdentity, 1, 1)); CGContextSetTextPosition(context, location.x, location.y); CFNumberRef featureValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value); CFTypeRef featureDictionaryKeys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue }; CFTypeRef featureDictionaryValues[] = { feature, featureValue }; CFDictionaryRef featureDictionary = CFDictionaryCreate(kCFAllocatorDefault, featureDictionaryKeys, featureDictionaryValues, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(featureValue); CFTypeRef featureSettingsValues[] = { featureDictionary }; CFArrayRef fontFeatureSettings = CFArrayCreate(kCFAllocatorDefault, featureSettingsValues, 1, &kCFTypeArrayCallBacks); CFRelease(featureDictionary); CFTypeRef fontDescriptorKeys[] = { kCTFontFeatureSettingsAttribute }; CFTypeRef fontDescriptorValues[] = { fontFeatureSettings }; CFDictionaryRef fontDescriptorAttributes = CFDictionaryCreate(kCFAllocatorDefault, fontDescriptorKeys, fontDescriptorValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(fontFeatureSettings); CTFontDescriptorRef modifiedFontDescriptor = CTFontDescriptorCreateCopyWithAttributes(fontDescriptor, fontDescriptorAttributes); CFRelease(fontDescriptorAttributes); CTFontRef font = CTFontCreateWithFontDescriptor(modifiedFontDescriptor, fontSize, nullptr); CFRelease(modifiedFontDescriptor); CFMutableStringRef string = CFStringCreateMutable(kCFAllocatorDefault, 0); CFStringAppend(string, feature); CFStringAppend(string, value ? CFSTR(" (on)") : CFSTR(" (off)")); CFStringAppend(string, CFSTR(": ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")); CGColorRef red = CGColorCreateGenericRGB(1, 0, 0, 1); CFTypeRef lineKeys[] = { kCTForegroundColorAttributeName }; CFTypeRef lineValues[] = { red }; CFDictionaryRef lineAttributes = CFDictionaryCreate(kCFAllocatorDefault, lineKeys, lineValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CGColorRelease(red); CFAttributedStringRef attributedString = CFAttributedStringCreate(kCFAllocatorDefault, string, lineAttributes); CFRelease(lineAttributes); CFRelease(string); CFMutableAttributedStringRef mutableAttributedString = CFAttributedStringCreateMutableCopy(kCFAllocatorDefault, 0, attributedString); CFRelease(attributedString); CTFontRef monospaceFont = CTFontCreateWithName(CFSTR("Courier"), fontSize, nullptr); CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(0, 12), kCTFontAttributeName, monospaceFont); CFRelease(monospaceFont); CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(12, 52), kCTFontAttributeName, font); CFRelease(font); CTLineRef line = CTLineCreateWithAttributedString(mutableAttributedString); CFRelease(mutableAttributedString); CTLineDraw(line, context); CFRelease(line); }
// This function is needed to work around a bug in Windows CG <rdar://problem/22703470> void PlatformCALayer::drawTextAtPoint(CGContextRef context, CGFloat x, CGFloat y, CGSize scale, CGFloat fontSize, const char* text, size_t length) const { auto matrix = CGAffineTransformMakeScale(scale.width, scale.height); auto font = adoptCF(CTFontCreateWithName(CFSTR("Helvetica"), fontSize, &matrix)); CFTypeRef keys[] = { kCTFontAttributeName, kCTForegroundColorFromContextAttributeName }; CFTypeRef values[] = { font.get(), kCFBooleanTrue }; auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); auto string = adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(text), length, kCFStringEncodingUTF8, false, kCFAllocatorNull)); auto attributedString = adoptCF(CFAttributedStringCreate(kCFAllocatorDefault, string.get(), attributes.get())); auto line = adoptCF(CTLineCreateWithAttributedString(attributedString.get())); CGContextSetTextPosition(context, x, y); CTLineDraw(line.get(), context); }
xy__Float draw__string(const char *s, int x, int y, int w, float pos) { CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, s, kCFStringEncodingUTF8); if (font == NULL || font_color == NULL) { fprintf(stderr, "Error in %s: need both font & font_color to be non-NULL.\n", __FUNCTION__); return x; } CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName }; CFTypeRef values[] = { font, font_color }; CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); CFRelease(string); CFRelease(attributes); CTLineRef line = CTLineCreateWithAttributedString(attrString); // This position set is needed to get a useful x_pos value on the next line. CGContextSetTextPosition(ctx, x, y); double x_pos = CTLineGetPenOffsetForFlush(line, pos, w); CGFloat descent; double line_width = CTLineGetTypographicBounds(line, NULL, &descent, NULL); CGContextSetTextPosition(ctx, x + x_pos, y + descent); CTLineDraw(line, ctx); CFRelease(line); return x + x_pos + line_width; }
static OSStatus local_CoreTR(CGContextRef ctx, CGRect r, CFStringRef string, CFStringRef fontName, CGFloat fontSize, TRFallbackBehavior fallbackBehavior, Boolean render, CGFloat baseline, TRInfo* oInfo) { if (!ctx || !string || !fontName) return paramErr; CGContextSaveGState(ctx); CGContextSetTextMatrix(ctx, CGAffineTransformIdentity); CTFontDescriptorRef fdesc = CTFontDescriptorCreateWithNameAndSize(fontName, fontSize); CTFontRef font = CTFontCreateWithFontDescriptor(fdesc, fontSize, NULL); CFIndex slen = CFStringGetLength(string); CFRange range = CFRangeMake(0L,slen); UniChar* buff = calloc(slen, sizeof(UniChar)); CFStringGetCharacters(string, range, buff); CGGlyph* glyphs = calloc(slen, sizeof(CGGlyph)); Boolean supported = CTFontGetGlyphsForCharacters(font, buff, glyphs, slen); //NSLog(@"%@ supported for '%@'? %d", fontName, string, supported); CFRelease(fdesc); if (!supported) { if (fallbackBehavior == TRLastResortFallbackBehavior) { CFRelease(font); fdesc = CTFontDescriptorCreateWithNameAndSize(CFSTR("LastResort"), fontSize); font = CTFontCreateWithFontDescriptor(fdesc, fontSize, NULL); CFRelease(fdesc); supported = true; } else { CFRange rng = CFRangeMake(0L, CFStringGetLength(string)); CTFontRef font2 = CTFontCreateForString(font, string, rng); CFRelease(font); CFStringRef fontName2 = CTFontCopyName(font2, kCTFontFullNameKey); fdesc = CTFontDescriptorCreateWithNameAndSize(fontName2, fontSize); //NSLog(@"falling back to %@ from %@ : %@", fontName2, fontName, fdesc); CFRelease(fontName2); font = CTFontCreateWithFontDescriptor(fdesc, fontSize, NULL); CFRelease(fdesc); supported = CTFontGetGlyphsForCharacters(font, buff, glyphs, slen); } } free(buff); free(glyphs); CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 1L, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(attrs, kCTFontAttributeName, font); CFAttributedStringRef attrStr = CFAttributedStringCreate(kCFAllocatorDefault, string, attrs); CFRelease(attrs); CTLineRef line = CTLineCreateWithAttributedString(attrStr); CFRelease(attrStr); CGRect bounds = CTLineGetImageBounds(line, ctx); CGFloat descent = CTFontGetDescent(font); CGFloat ascent = CTFontGetAscent(font); CFRelease(font); if (baseline < 0.0L) baseline = ((r.size.height - bounds.size.height + descent)/2.0L); if (render && (supported || fallbackBehavior != TRNoRenderFallbackBehavior)) { CGFloat x = r.origin.x + (r.size.width/2.0L) - (bounds.size.width/2.0L) - bounds.origin.x; CGFloat y = r.origin.y + baseline; CGPoint where = CGPointMake(x, y); CGContextSetTextPosition(ctx, where.x, where.y); CTLineDraw(line, ctx); } CFRelease(line); if (oInfo) { oInfo->height = bounds.size.height + descent; oInfo->width = bounds.size.width; oInfo->ascent = ascent; oInfo->descent = descent; oInfo->baseline = baseline; oInfo->fontSupported = supported; } CGContextRestoreGState(ctx); return (supported)? noErr:kATSUFontsNotMatched; }
static bool font_renderer_create_atlas(CTFontRef face, ct_font_renderer_t *handle) { int max_width, max_height; unsigned i; size_t bytesPerRow; CGGlyph glyphs[CT_ATLAS_SIZE]; CGRect bounds[CT_ATLAS_SIZE]; CGSize advances[CT_ATLAS_SIZE]; float ascent, descent; CGContextRef offscreen; CFDictionaryRef attr; CFTypeRef values[1]; CFStringRef keys[1]; void *bitmapData = NULL; bool ret = true; size_t bitsPerComponent = 8; UniChar characters[CT_ATLAS_SIZE] = {0}; values[0] = face; keys[0] = kCTFontAttributeName; for (i = 0; i < CT_ATLAS_SIZE; i++) characters[i] = (UniChar)i; CTFontGetGlyphsForCharacters(face, characters, glyphs, CT_ATLAS_SIZE); CTFontGetBoundingRectsForGlyphs(face, #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 kCTFontOrientationDefault, #else kCTFontDefaultOrientation, #endif glyphs, bounds, CT_ATLAS_SIZE); CTFontGetAdvancesForGlyphs(face, #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 kCTFontOrientationDefault, #else kCTFontDefaultOrientation, #endif glyphs, advances, CT_ATLAS_SIZE); ascent = CTFontGetAscent(face); descent = CTFontGetDescent(face); max_width = 0; max_height = 0; for (i = 0; i < CT_ATLAS_SIZE; i++) { int origin_x, origin_y; struct font_glyph *glyph = &handle->glyphs[i]; if (!glyph) continue; origin_x = ceil(bounds[i].origin.x); origin_y = ceil(bounds[i].origin.y); glyph->draw_offset_x = 0; glyph->draw_offset_y = -ascent; glyph->width = ceil(bounds[i].size.width); glyph->height = ceil(bounds[i].size.height); glyph->advance_x = ceil(advances[i].width); glyph->advance_y = ceil(advances[i].height); max_width = MAX(max_width, (origin_x + glyph->width)); max_height = MAX(max_height, (origin_y + glyph->height)); } max_height = MAX(max_height, ceil(ascent+descent)); handle->atlas.width = max_width * CT_ATLAS_COLS; handle->atlas.height = max_height * CT_ATLAS_ROWS; handle->atlas.buffer = (uint8_t*) calloc(handle->atlas.width * handle->atlas.height, 1); if (!handle->atlas.buffer) { ret = false; goto end; } bytesPerRow = max_width; bitmapData = calloc(max_height, bytesPerRow); offscreen = CGBitmapContextCreate(bitmapData, max_width, max_height, bitsPerComponent, bytesPerRow, NULL, kCGImageAlphaOnly); CGContextSetTextMatrix(offscreen, CGAffineTransformIdentity); attr = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); for (i = 0; i < CT_ATLAS_SIZE; i++) { char glyph_cstr[2]; const uint8_t *src; uint8_t *dst; unsigned offset_x, offset_y, r, c; CFStringRef glyph_cfstr; CFAttributedStringRef attrString; CTLineRef line; struct font_glyph *glyph = &handle->glyphs[i]; if (!glyph) continue; glyph->width = max_width; glyph->height = max_height; offset_x = (i % CT_ATLAS_COLS) * max_width; offset_y = (i / CT_ATLAS_COLS) * max_height; glyph->atlas_offset_x = offset_x; glyph->atlas_offset_y = offset_y; glyph_cstr[0] = i; glyph_cstr[1] = 0; glyph_cfstr = CFStringCreateWithCString( NULL, glyph_cstr, kCFStringEncodingASCII ); attrString = CFAttributedStringCreate(NULL, glyph_cfstr, attr); CFRelease(glyph_cfstr); glyph_cfstr = NULL; line = CTLineCreateWithAttributedString(attrString); CFRelease(attrString); attrString = NULL; memset( bitmapData, 0, max_height * bytesPerRow ); CGContextSetTextPosition(offscreen, 0, descent); CTLineDraw(line, offscreen); CGContextFlush( offscreen ); CFRelease( line ); line = NULL; dst = (uint8_t*)handle->atlas.buffer; src = (const uint8_t*)bitmapData; for (r = 0; r < max_height; r++ ) { for (c = 0; c < max_width; c++) { unsigned src_idx = r * bytesPerRow + c; unsigned dest_idx = (r + offset_y) * (CT_ATLAS_COLS * max_width) + (c + offset_x); uint8_t v = src[src_idx]; dst[dest_idx] = v; } } } CFRelease(attr); CGContextRelease(offscreen); attr = NULL; offscreen = NULL; free(bitmapData); end: return ret; }
Memsubfont* mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias) { CFStringRef s; CGColorSpaceRef color; CGContextRef ctxt; CTFontRef font; CTFontDescriptorRef desc; CGRect bbox; Memimage *m, *mc, *m1; int x, y, y0; int i, height, ascent; Fontchar *fc, *fc0; Memsubfont *sf; CGFloat whitef[] = { 1.0, 1.0 }; CGColorRef white; s = c2mac(name); desc = CTFontDescriptorCreateWithNameAndSize(s, size); CFRelease(s); if(desc == nil) return nil; font = CTFontCreateWithFontDescriptor(desc, 0, nil); CFRelease(desc); if(font == nil) return nil; bbox = CTFontGetBoundingBox(font); x = (int)(bbox.size.width*2 + 0.99999999); fontheight(f, size, &height, &ascent); y = height; y0 = height - ascent; m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), GREY8); if(m == nil) return nil; mc = allocmemimage(Rect(0, 0, x+1, y+1), GREY8); if(mc == nil){ freememimage(m); return nil; } memfillcolor(m, DBlack); memfillcolor(mc, DBlack); fc = malloc((hi+2 - lo) * sizeof fc[0]); sf = malloc(sizeof *sf); if(fc == nil || sf == nil) { freememimage(m); freememimage(mc); free(fc); free(sf); return nil; } fc0 = fc; color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8, mc->width*sizeof(u32int), color, kCGImageAlphaNone); white = CGColorCreate(color, whitef); CGColorSpaceRelease(color); if(ctxt == nil) { freememimage(m); freememimage(mc); free(fc); free(sf); return nil; } CGContextSetAllowsAntialiasing(ctxt, antialias); CGContextSetTextPosition(ctxt, 0, 0); // XXX #if OSX_VERSION >= 101400 CGContextSetAllowsFontSmoothing(ctxt, false); #endif x = 0; for(i=lo; i<=hi; i++, fc++) { char buf[20]; CFStringRef str; CFDictionaryRef attrs; CFAttributedStringRef attrString; CTLineRef line; CGRect r; CGPoint p1; CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName }; CFTypeRef values[] = { font, white }; sprint(buf, "%C", (Rune)mapUnicode(name, i)); str = c2mac(buf); // See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2 attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs); CFRelease(str); CFRelease(attrs); line = CTLineCreateWithAttributedString(attrString); CGContextSetTextPosition(ctxt, 0, y0); r = CTLineGetImageBounds(line, ctxt); memfillcolor(mc, DBlack); CTLineDraw(line, ctxt); CFRelease(line); fc->x = x; fc->top = 0; fc->bottom = Dy(m->r); // fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y); p1 = CGContextGetTextPosition(ctxt); if(p1.x <= 0 || mapUnicode(name, i) == 0xfffd) { fc->width = 0; fc->left = 0; if(i == 0) { drawpjw(m, fc, x, (int)(bbox.size.width + 0.99999999), y, y - y0); x += fc->width; } continue; } memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S); fc->width = p1.x; fc->left = 0; x += p1.x; } fc->x = x; // round up to 32-bit boundary // so that in-memory data is same // layout as in-file data. if(x == 0) x = 1; if(y == 0) y = 1; if(antialias) x += -x & 3; else x += -x & 31; m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1); memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S); freememimage(m); freememimage(mc); sf->name = nil; sf->n = hi+1 - lo; sf->height = Dy(m1->r); sf->ascent = Dy(m1->r) - y0; sf->info = fc0; sf->bits = m1; return sf; }