#include "Draw.h" #define LLOG(x) // LOG(x) #define LTIMING(x) // TIMING(x) #if !defined(CUSTOM_FONTSYS) && defined(PLATFORM_COCOA) #ifndef flagNOMM // Removes ObjectiveC and AppKit dependence in Draw (but disables Fonts) #define Point NS_Point #define Rect NS_Rect #define Size NS_Size #include #undef Point #undef Rect #undef Size namespace Upp { namespace Detail { // TODO: following utilities are normally defined in CtrlCore, rename to avoid name clash template struct CFRef { T ptr; T operator~() { return ptr; } operator T() { return ptr; } T operator->() { return ptr; } T Detach() { T h = ptr; ptr = NULL; return h; } CFRef(T p) { ptr = p; } ~CFRef() { if(ptr) CFRelease(ptr); } }; struct AutoreleasePool { NSAutoreleasePool *pool; AutoreleasePool() { pool = [[NSAutoreleasePool alloc] init]; } ~AutoreleasePool() { [pool release]; } }; }; using namespace Detail; WString ToWString(CFStringRef s) { if(!s) return Null; CFIndex l = CFStringGetLength(s); if(!l) return Null; Buffer h(l); CFStringGetCharacters(s, CFRangeMake(0, l), (UniChar *)~h); return ToUtf32(~h, l); } String ToString(CFStringRef s) { return ToWString(s).ToString(); } CTFontRef CT_Font0(Font fnt, bool& synth) { CFRef s = CFStringCreateWithCString(NULL, ~fnt.GetFaceName(), kCFStringEncodingUTF8); CFRef ctfont0 = CTFontCreateWithName(s, fnt.GetHeight(), NULL); synth = false; if(fnt.IsItalic() || fnt.IsBold()) { CTFontSymbolicTraits t = 0; if(fnt.IsBold()) t |= kCTFontBoldTrait; if(fnt.IsItalic()) t |= kCTFontItalicTrait; CFRef ctfont = CTFontCreateCopyWithSymbolicTraits(ctfont0, fnt.GetHeight(), NULL, t, t); if(ctfont) return ctfont.Detach(); synth = true; } return ctfont0.Detach(); } CTFontRef CT_Font(Font fnt, bool& synth) { struct Entry { Font font; CTFontRef ctfont = NULL; bool synth = false; void Free() { if(ctfont) CFRelease(ctfont); ctfont = NULL; } Entry() { font.Height(-22222); } ~Entry() { Free(); } }; const int FONTCACHE = 64; static Entry cache[FONTCACHE]; for(int i = 0; i < FONTCACHE; i++) if(cache[i].font == fnt) { synth = cache[i].synth; return cache[i].ctfont; } Entry& e = cache[Random(FONTCACHE)]; e.Free(); e.font = fnt; e.ctfont = CT_Font0(fnt, synth); e.synth = synth; return e.ctfont; } CGGlyph GetCharGlyph(CTFontRef ctfont, int chr) { CGGlyph glyph_index[2]; Vector h = ToUtf16(chr); CTFontGetGlyphsForCharacters(ctfont, (UniChar *)h.begin(), glyph_index, h.GetCount()); return glyph_index[0]; } GlyphInfo GetGlyphInfoSys(CTFontRef ctfont, int chr, bool bold_synth, CGRect *bounds = NULL) { GlyphInfo gi; gi.lspc = gi.rspc = 0; gi.width = 0x8000; if(ctfont) { LTIMING("GetGlyphInfoSys 2"); CGGlyph glyph_index = GetCharGlyph(ctfont, chr); if(glyph_index) { CGSize advance; CTFontGetAdvancesForGlyphs(ctfont, kCTFontOrientationHorizontal, &glyph_index, &advance, 1); gi.width = ceil(advance.width); gi.lspc = gi.rspc = 0; // TODO! (using bounding box?) gi.glyphi = glyph_index; if(bold_synth) gi.width++; if(bounds) CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontOrientationHorizontal, &glyph_index, bounds, 1); } } return gi; } CommonFontInfo GetFontInfoSys(Font font) { CommonFontInfo fi; String path; bool synth; CTFontRef ctfont = CT_Font(font, synth); if(ctfont) { #if 0 DDUMP(font); DDUMP(CTFontGetSize(ctfont)); DDUMP(CTFontGetAscent(ctfont)); DDUMP(CTFontGetDescent(ctfont)); DDUMP(); DDUMP(CTFontGetXHeight(ctfont)); DDUMP(CTFontGetUnderlinePosition(ctfont)); DDUMP(MakeRect(CTFontGetBoundingBox(ctfont))); DDUMPHEX(CTFontGetSymbolicTraits(ctfont)); DDUMP(font); CGRect cr = CTFontGetBoundingBox(ctfont); DDUMP(cr.origin.y); DDUMP(cr.size.height); DDUMP(cr.origin.y + cr.size.height); #endif fi.descent = ceil(CTFontGetDescent(ctfont)); fi.ascent = ceil(CTFontGetAscent(ctfont)); fi.external = ceil(CTFontGetLeading(ctfont)); // Some MacOS fonts have really weird ascent/descents (namely stadard GUI font...) // let us fix it by testing typical charactes bounding boxes static WString descent_test = "yjgp"; CGRect bb; for(int i = 0; i < descent_test.GetCount(); i++) if(GetGlyphInfoSys(ctfont, descent_test[i], synth && font.IsBold(), &bb).IsNormal()) fi.descent = max(fi.descent, (int)ceil(-bb.origin.y)); int ascent = fi.ascent; static WString ascent_test = "ÀÁÂÃÄË"; for(int i = 0; i < ascent_test.GetCount(); i++) if(GetGlyphInfoSys(ctfont, ascent_test[i], synth && font.IsBold(), &bb).IsNormal()) ascent = max(ascent, (int)ceil(bb.origin.y + bb.size.height)); fi.ascent = ascent; fi.internal = 0; fi.overhang = 0; fi.maxwidth = GetGlyphInfoSys(ctfont, 'W', synth && font.IsBold()).width; // TODO? fi.avewidth = GetGlyphInfoSys(ctfont, 'e', synth && font.IsBold()).width; fi.default_char = '?'; fi.fixedpitch = CTFontGetSymbolicTraits(ctfont) & kCTFontMonoSpaceTrait; fi.ttf = true; CFRef fd = CTFontCopyFontDescriptor(ctfont); CFRef url = (CFURLRef)CTFontDescriptorCopyAttribute(fd, kCTFontURLAttribute); CFRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); String p = ToString(path); if(p.GetCount() < 250) strcpy(fi.path, ~p); else *fi.path = 0; } return fi; } GlyphInfo GetGlyphInfoSys(Font font, int chr) { LTIMING("GetGlyphInfoSys"); bool synth; CTFontRef ctfont = CT_Font(font, synth); return GetGlyphInfoSys(ctfont, chr, synth && font.IsBold()); } Vector GetAllFacesSys() { Index facename; facename.Add("Arial"); // This is default GUI font, changed afterward facename.Add("Times New Roman"); facename.Add("Arial"); facename.Add("Courier New"); int oi = facename.GetCount(); AutoreleasePool __; CFRef collection = CTFontCollectionCreateFromAvailableFonts(0); if(collection) { CFRef fonts = CTFontCollectionCreateMatchingFontDescriptors(collection); if(fonts) { int count = CFArrayGetCount(fonts); for(int i = 0; i < count; ++i) { CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i); CFRef family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); facename.FindAdd(ToString(family_name)); } } } Vector h = facename.PickKeys(); Sort(SubRange(h, oi, h.GetCount() - oi)); Vector r; for(String s : h) { FaceInfo& fi = r.Add(); fi.name = s; fi.info = Font::TTF; CFRef fs = CFStringCreateWithCString(NULL, ~s, kCFStringEncodingUTF8); CFRef ctfont = CTFontCreateWithName(fs, 12, NULL); dword traits = CTFontGetSymbolicTraits(ctfont); fi.info = Font::SCALEABLE; if(traits & kCTFontMonoSpaceTrait) fi.info |= Font::FIXEDPITCH; switch(traits & kCTFontClassMaskTrait) { case kCTFontOldStyleSerifsClass: case kCTFontTransitionalSerifsClass: case kCTFontModernSerifsClass: case kCTFontClarendonSerifsClass: case kCTFontSlabSerifsClass: case kCTFontFreeformSerifsClass: fi.info |= Font::SERIFSTYLE; break; case kCTFontScriptsClass: fi.info |= Font::SCRIPTSTYLE; break; } } return r; } String GetFontDataSys(Font font, const char *table, int offset, int size) { // read truetype or opentype table FileIn in(font.Fi().path); int q = in.Get32be(); if(q == 0x74746366) { // font collection in.Get32(); // skip major/minor version int nfonts = in.Get32be(); if(font.Fi().fonti >= nfonts) return Null; in.SeekCur(font.Fi().fonti * 4); int offset = in.Get32be(); if(offset < 0 || offset >= in.GetSize()) return Null; in.Seek(offset); q = in.Get32be(); } if(q != 0x74727565 && q != 0x00010000 && q != 0x4f54544f) // 0x4f54544f means CCF font! return Null; int n = in.Get16be(); in.Get32(); in.Get16(); while(n--) { if(in.IsError() || in.IsEof()) return Null; String tab = in.Get(4); in.Get32(); int off = in.Get32be(); int len = in.Get32be(); if(tab == table) { if(off < 0 || len < 0 || off + len > in.GetSize()) return Null; len = min(len - offset, size); if(len < 0) return Null; in.Seek(off + offset); return in.Get(len); } } return Null; } struct sCGPathTarget { double x, y; FontGlyphConsumer *sw; }; static void convertCGPathToQPainterPath(void *info, const CGPathElement *e) { auto t = (sCGPathTarget *)info; switch(e->type) { case kCGPathElementMoveToPoint: t->sw->Move(Pointf(e->points[0].x + t->x, e->points[0].y + t->y)); break; case kCGPathElementAddLineToPoint: t->sw->Line(Pointf(e->points[0].x + t->x, e->points[0].y + t->y)); break; case kCGPathElementAddQuadCurveToPoint: t->sw->Quadratic(Pointf(e->points[0].x + t->x, e->points[0].y + t->y), Pointf(e->points[1].x + t->x, e->points[1].y + t->y)); break; case kCGPathElementAddCurveToPoint: t->sw->Cubic(Pointf(e->points[0].x + t->x, e->points[0].y + t->y), Pointf(e->points[1].x + t->x, e->points[1].y + t->y), Pointf(e->points[2].x + t->x, e->points[2].y + t->y)); break; case kCGPathElementCloseSubpath: t->sw->Close(); break; } } void RenderCharacterSys(FontGlyphConsumer& sw, double x, double y, int chr, Font font) { CGAffineTransform cgMatrix = CGAffineTransformIdentity; cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1); bool synth; CTFontRef ctfont = CT_Font(font, synth); CGGlyph glyph_index = GetCharGlyph(ctfont, chr); CFRef cgpath = CTFontCreatePathForGlyph(ctfont, glyph_index, &cgMatrix); sCGPathTarget t; t.x = x; t.y = y + font.GetAscent(); t.sw = &sw; CGPathApply(cgpath, &t, convertCGPathToQPainterPath); } }; #endif #endif