参考文档:
https://en.wikipedia.org/wiki/Core_Text
https://developer.apple.com/library/content/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html#//apple_ref/doc/uid/TP40009459-CH5-SW18
https://developer.apple.com/library/content/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF
https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005533
#include <iostream>
#include <string>
#include <CoreText/CoreText.h>
#include <CoreFoundation/CFBase.h>
#include <iomanip>
void GetTextExtent(const std::wstring fontname, int fontsize, const std::wstring str, float* width, float* height) {
if (str.empty())
{
*width = 0;
*height = 0;
return;
}
CFStringRef nameRef = CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8*>(fontname.c_str()),
fontname.size() * sizeof(wchar_t),
kCFStringEncodingUTF32LE,
false);
CTFontRef fontRef = CTFontCreateWithName(nameRef, fontsize, 0);
CFStringRef strRef = CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8*>(str.c_str()),
str.size() * sizeof(wchar_t),
kCFStringEncodingUTF32LE,
false);
CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { fontRef };
CFDictionaryRef font_attributes = CFDictionaryCreate(0, (const void **)keys, (const void **)values, sizeof(keys)/sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFAttributedStringRef attrStr = CFAttributedStringCreate(0, strRef, font_attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrStr);
CGFloat a, d, l, w;
w = CTLineGetTypographicBounds(line, &a, &d, &l);
if (width)
*width = w;
if (height)
*height = a + d + l;
CFRelease(nameRef);
CFRelease(strRef);
CFRelease(fontRef);
}
void GetTextExtent2(const std::wstring fontname, int fontsize, const std::wstring str, float* width, float* height) {
if (str.empty())
{
*width = 0;
*height = 0;
return;
}
CFStringRef strRef = CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8*>(str.c_str()),
str.size() * sizeof(wchar_t),
kCFStringEncodingUTF32LE,
false);
CFStringRef nameRef = CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8*>(fontname.c_str()),
fontname.size() * sizeof(wchar_t),
kCFStringEncodingUTF32LE,
false);
CTFontRef fontRef = CTFontCreateWithName(nameRef, fontsize, 0);
CFIndex count = CFStringGetLength(strRef);
UniChar *characters = (UniChar *)malloc(sizeof(UniChar) * count);
CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * count);
CFStringGetCharacters(strRef, CFRangeMake(0, count), characters);
CTFontGetGlyphsForCharacters(fontRef, characters, glyphs, count);
CGSize *sizeCore = (CGSize*)malloc(count * sizeof(CGSize));
double adv = CTFontGetAdvancesForGlyphs (fontRef,
kCTFontOrientationHorizontal,
glyphs,
sizeCore,
count);
*width = adv;
*height = sizeCore->height;
CFRelease(strRef);
CFRelease(nameRef);
CFRelease(fontRef);
free(characters);
free(glyphs);
free(sizeCore);
}
void GetTextExtent3(const std::wstring fontname, int fontsize, const std::wstring str, float* width, float* height) {
if (str.empty())
{
*width = 0;
*height = 0;
return;
}
CFStringRef strRef = CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8*>(str.c_str()),
str.size() * sizeof(wchar_t),
kCFStringEncodingUTF32LE,
false);
CFStringRef nameRef = CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8*>(fontname.c_str()),
fontname.size() * sizeof(wchar_t),
kCFStringEncodingUTF32LE,
false);
CTFontRef fontRef = CTFontCreateWithName(nameRef, fontsize, 0);
CFIndex count = CFStringGetLength(strRef);
UniChar *characters = (UniChar *)malloc(sizeof(UniChar) * count);
CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * count);
CFStringGetCharacters(strRef, CFRangeMake(0, count), characters);
CTFontGetGlyphsForCharacters(fontRef, characters, glyphs, count);
CGSize *sizeCore = (CGSize*)malloc(count * sizeof(CGSize));
CGRect *rectCore = (CGRect*)malloc(count * sizeof(CGRect));
double adv = CTFontGetAdvancesForGlyphs (fontRef,
kCTFontOrientationHorizontal,
glyphs,
sizeCore,
count);
// rect 是第一个字符的宽度
CGRect rect = CTFontGetBoundingRectsForGlyphs (fontRef,
kCTFontOrientationHorizontal,
glyphs,
rectCore,
count);
double kerning = adv - rect.size.width;
std::cout << "return CGRect.size.width : " << ((CGRect*)rectCore)->size.width << std::endl;
std::cout << "CGRect : ";
float sumwidth = 0.f;
for (int i = 0; i < count; ++i) {
sumwidth += rect.size.width;
std::cout << " " << i << " : " << rect.size.width;
}
std::cout << std::endl;
std::wcout << str << "\tsum : " << sumwidth << std::endl;
std::wcout << str << "\tadv : " << adv << std::endl;
std::wcout << str << "\tkerning: " << kerning << std::endl;
free(characters);
free(glyphs);
free(sizeCore);
free(rectCore);
}
typedef void (*func)(const std::wstring fontname, int fontsize, const std::wstring str, float* width, float* height);
void test1(func f) {
std::wstring fontname = L"Arial";
int fontsize = 16;
std::wstring str;
for (wchar_t wc = 'A'; wc < 'z'; ++wc ) {
str.append(&wc , 1);
float w, h;
f(fontname, fontsize, str, &w, &h);
std::wcout << str << "\t" << fontname << "\t" << fontsize << "\t" << std::setw(10) << w << "\t" << h << "\t" << std::endl;
}
}
void test2(func f) {
std::wstring fontname = L"Arial";
int fontsize = 16;
for (wchar_t wc = 'A'; wc < 'z'; ++wc ) {
std::wstring str;
str.append(&wc , 1);
float w, h;
f(fontname, fontsize, str, &w, &h);
std::wcout << str << "\t" << fontname << "\t" << fontsize << "\t" << std::setw(10) << w << "\t" << h << "\t" << std::endl;
}
}