DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame^] | 1 | // |
| 2 | // "$Id: fl_font_mac.cxx 8597 2011-04-17 13:18:55Z ianmacarthur $" |
| 3 | // |
| 4 | // MacOS font selection routines for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2011 by Bill Spitzak and others. |
| 7 | // |
| 8 | // This library is free software; you can redistribute it and/or |
| 9 | // modify it under the terms of the GNU Library General Public |
| 10 | // License as published by the Free Software Foundation; either |
| 11 | // version 2 of the License, or (at your option) any later version. |
| 12 | // |
| 13 | // This library is distributed in the hope that it will be useful, |
| 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | // Library General Public License for more details. |
| 17 | // |
| 18 | // You should have received a copy of the GNU Library General Public |
| 19 | // License along with this library; if not, write to the Free Software |
| 20 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| 21 | // USA. |
| 22 | // |
| 23 | // Please report all bugs and problems on the following page: |
| 24 | // |
| 25 | // http://www.fltk.org/str.php |
| 26 | // |
| 27 | |
| 28 | #include <config.h> |
| 29 | |
| 30 | /* from fl_utf.c */ |
| 31 | extern unsigned fl_utf8toUtf16(const char* src, unsigned srclen, unsigned short* dst, unsigned dstlen); |
| 32 | |
| 33 | static CGAffineTransform font_mx = { 1, 0, 0, -1, 0, 0 }; |
| 34 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 35 | static CFMutableDictionaryRef attributes = NULL; |
| 36 | #endif |
| 37 | |
| 38 | Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name, Fl_Fontsize Size) { |
| 39 | next = 0; |
| 40 | # if HAVE_GL |
| 41 | listbase = 0; |
| 42 | # endif |
| 43 | |
| 44 | // knowWidths = 0; |
| 45 | // OpenGL needs those for its font handling |
| 46 | q_name = strdup(name); |
| 47 | size = Size; |
| 48 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 49 | if (fl_mac_os_version >= 100500) {//unfortunately, CTFontCreateWithName != NULL on 10.4 also! |
| 50 | CFStringRef str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); |
| 51 | fontref = CTFontCreateWithName(str, size, NULL); |
| 52 | CGGlyph glyph[2]; |
| 53 | const UniChar A[2]={'W','.'}; |
| 54 | CTFontGetGlyphsForCharacters(fontref, A, glyph, 2); |
| 55 | CGSize advances[2]; |
| 56 | double w; |
| 57 | CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, advances, 2); |
| 58 | w = advances[0].width; |
| 59 | if ( abs(advances[0].width - advances[1].width) < 1E-2 ) {//this is a fixed-width font |
| 60 | // slightly rescale fixed-width fonts so the character width has an integral value |
| 61 | CFRelease(fontref); |
| 62 | CGFloat fsize = size / ( w/floor(w + 0.5) ); |
| 63 | fontref = CTFontCreateWithName(str, fsize, NULL); |
| 64 | w = CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, NULL, 1); |
| 65 | } |
| 66 | CFRelease(str); |
| 67 | ascent = (short)(CTFontGetAscent(fontref) + 0.5); |
| 68 | descent = (short)(CTFontGetDescent(fontref) + 0.5); |
| 69 | q_width = w + 0.5; |
| 70 | for (unsigned i = 0; i < sizeof(width)/sizeof(float*); i++) width[i] = NULL; |
| 71 | if (!attributes) { |
| 72 | static CFNumberRef zero_ref; |
| 73 | float zero = 0.; |
| 74 | zero_ref = CFNumberCreate(NULL, kCFNumberFloat32Type, &zero); |
| 75 | // deactivate kerning for all fonts, so that string width = sum of character widths |
| 76 | // which allows fast fl_width() implementation. |
| 77 | attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, |
| 78 | 3, |
| 79 | &kCFTypeDictionaryKeyCallBacks, |
| 80 | &kCFTypeDictionaryValueCallBacks); |
| 81 | CFDictionarySetValue (attributes, kCTKernAttributeName, zero_ref); |
| 82 | } |
| 83 | if (ascent == 0) { // this may happen with some third party fonts |
| 84 | CFDictionarySetValue (attributes, kCTFontAttributeName, fontref); |
| 85 | CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("Wj"), attributes); |
| 86 | CTLineRef ctline = CTLineCreateWithAttributedString(mastr); |
| 87 | CFRelease(mastr); |
| 88 | CGFloat fascent, fdescent; |
| 89 | CTLineGetTypographicBounds(ctline, &fascent, &fdescent, NULL); |
| 90 | CFRelease(ctline); |
| 91 | ascent = (short)(fascent + 0.5); |
| 92 | descent = (short)(fdescent + 0.5); |
| 93 | } |
| 94 | } |
| 95 | else { |
| 96 | #endif |
| 97 | #if ! __LP64__ |
| 98 | OSStatus err; |
| 99 | // fill our structure with a few default values |
| 100 | ascent = Size*3/4; |
| 101 | descent = Size-ascent; |
| 102 | q_width = Size*2/3; |
| 103 | // now use ATS to get the actual Glyph size information |
| 104 | // say that our passed-in name is encoded as UTF-8, since this works for plain ASCII names too... |
| 105 | CFStringRef cfname = CFStringCreateWithCString(0L, name, kCFStringEncodingUTF8); |
| 106 | ATSFontRef font = ATSFontFindFromName(cfname, kATSOptionFlagsDefault); |
| 107 | if (font) { |
| 108 | ATSFontMetrics m = { 0 }; |
| 109 | ATSFontGetHorizontalMetrics(font, kATSOptionFlagsDefault, &m); |
| 110 | if (m.avgAdvanceWidth) q_width = int(m.avgAdvanceWidth*Size); |
| 111 | // playing with the offsets a little to make standard sizes fit |
| 112 | if (m.ascent) ascent = int(m.ascent*Size-0.5f); |
| 113 | if (m.descent) descent = -int(m.descent*Size-1.5f); |
| 114 | } |
| 115 | CFRelease(cfname); |
| 116 | // now we allocate everything needed to render text in this font later |
| 117 | // get us the default layout and style |
| 118 | err = ATSUCreateTextLayout(&layout); |
| 119 | UniChar mTxt[2] = { 65, 0 }; |
| 120 | err = ATSUSetTextPointerLocation(layout, mTxt, kATSUFromTextBeginning, 1, 1); |
| 121 | err = ATSUCreateStyle(&style); |
| 122 | err = ATSUSetRunStyle(layout, style, kATSUFromTextBeginning, kATSUToTextEnd); |
| 123 | // now set the actual font, size and attributes. We also set the font matrix to |
| 124 | // render our font up-side-down, so when rendered through our inverted CGContext, |
| 125 | // text will appear normal again. |
| 126 | Fixed fsize = IntToFixed(Size); |
| 127 | // ATSUFontID fontID = FMGetFontFromATSFontRef(font); |
| 128 | ATSUFontID fontID; |
| 129 | ATSUFindFontFromName(name, strlen(name), kFontFullName, kFontMacintoshPlatform, kFontRomanScript, kFontEnglishLanguage, &fontID); |
| 130 | |
| 131 | // draw the font upside-down... Compensate for fltk/OSX origin differences |
| 132 | ATSUAttributeTag sTag[] = { kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag }; |
| 133 | ByteCount sBytes[] = { sizeof(ATSUFontID), sizeof(Fixed), sizeof(CGAffineTransform) }; |
| 134 | ATSUAttributeValuePtr sAttr[] = { &fontID, &fsize, &font_mx }; |
| 135 | err = ATSUSetAttributes(style, 3, sTag, sBytes, sAttr); |
| 136 | // next, make sure that Quartz will only render at integer coordinates |
| 137 | ATSLineLayoutOptions llo = kATSLineUseDeviceMetrics | kATSLineDisableAllLayoutOperations; |
| 138 | ATSUAttributeTag aTag[] = { kATSULineLayoutOptionsTag }; |
| 139 | ByteCount aBytes[] = { sizeof(ATSLineLayoutOptions) }; |
| 140 | ATSUAttributeValuePtr aAttr[] = { &llo }; |
| 141 | err = ATSUSetLineControls (layout, kATSUFromTextBeginning, 1, aTag, aBytes, aAttr); |
| 142 | // now we are finally ready to measure some letter to get the bounding box |
| 143 | Fixed bBefore, bAfter, bAscent, bDescent; |
| 144 | err = ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, 1, &bBefore, &bAfter, &bAscent, &bDescent); |
| 145 | // Requesting a certain height font on Mac does not guarantee that ascent+descent |
| 146 | // equal the requested height. fl_height will reflect the actual height that we got. |
| 147 | // The font "Apple Chancery" is a pretty extreme example of overlapping letters. |
| 148 | float fa = -FixedToFloat(bAscent), fd = -FixedToFloat(bDescent); |
| 149 | if (fa>0.0f && fd>0.0f) { |
| 150 | //float f = Size/(fa+fd); |
| 151 | ascent = int(fa); //int(fa*f+0.5f); |
| 152 | descent = int(fd); //Size - ascent; |
| 153 | } |
| 154 | int w = FixedToInt(bAfter); |
| 155 | if (w) |
| 156 | q_width = FixedToInt(bAfter); |
| 157 | |
| 158 | # define ENABLE_TRANSIENT_FONTS 1 |
| 159 | |
| 160 | # ifdef ENABLE_TRANSIENT_FONTS |
| 161 | // Now, by way of experiment, try enabling Transient Font Matching, this will |
| 162 | // cause ATSU to find a suitable font to render any chars the current font can't do... |
| 163 | ATSUSetTransientFontMatching (layout, true); |
| 164 | # endif |
| 165 | #endif//__LP64__ |
| 166 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 167 | } |
| 168 | #endif |
| 169 | } |
| 170 | |
| 171 | Fl_Font_Descriptor::~Fl_Font_Descriptor() { |
| 172 | /* |
| 173 | #if HAVE_GL |
| 174 | // ++ todo: remove OpenGL font alocations |
| 175 | // Delete list created by gl_draw(). This is not done by this code |
| 176 | // as it will link in GL unnecessarily. There should be some kind |
| 177 | // of "free" routine pointer, or a subclass? |
| 178 | // if (listbase) { |
| 179 | // int base = font->min_char_or_byte2; |
| 180 | // int size = font->max_char_or_byte2-base+1; |
| 181 | // int base = 0; int size = 256; |
| 182 | // glDeleteLists(listbase+base,size); |
| 183 | // } |
| 184 | #endif |
| 185 | */ |
| 186 | if (this == fl_graphics_driver->font_descriptor()) fl_graphics_driver->font_descriptor(NULL); |
| 187 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 188 | if (fl_mac_os_version >= 100500) { |
| 189 | CFRelease(fontref); |
| 190 | for (unsigned i = 0; i < sizeof(width)/sizeof(float*); i++) { |
| 191 | if (width[i]) free(width[i]); |
| 192 | } |
| 193 | } |
| 194 | #endif |
| 195 | } |
| 196 | |
| 197 | //////////////////////////////////////////////////////////////// |
| 198 | |
| 199 | static Fl_Fontdesc built_in_table[] = { |
| 200 | {"Arial"}, |
| 201 | {"Arial Bold"}, |
| 202 | {"Arial Italic"}, |
| 203 | {"Arial Bold Italic"}, |
| 204 | {"Courier New"}, |
| 205 | {"Courier New Bold"}, |
| 206 | {"Courier New Italic"}, |
| 207 | {"Courier New Bold Italic"}, |
| 208 | {"Times New Roman"}, |
| 209 | {"Times New Roman Bold"}, |
| 210 | {"Times New Roman Italic"}, |
| 211 | {"Times New Roman Bold Italic"}, |
| 212 | {"Symbol"}, |
| 213 | {"Monaco"}, |
| 214 | {"Andale Mono"}, // there is no bold Monaco font on standard Mac |
| 215 | {"Webdings"}, |
| 216 | }; |
| 217 | |
| 218 | static UniChar *utfWbuf = 0; |
| 219 | static unsigned utfWlen = 0; |
| 220 | |
| 221 | static UniChar *mac_Utf8_to_Utf16(const char *txt, int len, int *new_len) |
| 222 | { |
| 223 | unsigned wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); |
| 224 | if (wlen >= utfWlen) |
| 225 | { |
| 226 | utfWlen = wlen + 100; |
| 227 | if (utfWbuf) free(utfWbuf); |
| 228 | utfWbuf = (UniChar*)malloc((utfWlen)*sizeof(UniChar)); |
| 229 | wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); |
| 230 | } |
| 231 | *new_len = wlen; |
| 232 | return utfWbuf; |
| 233 | } // mac_Utf8_to_Utf16 |
| 234 | |
| 235 | Fl_Fontdesc* fl_fonts = built_in_table; |
| 236 | |
| 237 | static Fl_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size) { |
| 238 | Fl_Fontdesc* s = fl_fonts+fnum; |
| 239 | if (!s->name) s = fl_fonts; // use 0 if fnum undefined |
| 240 | Fl_Font_Descriptor* f; |
| 241 | for (f = s->first; f; f = f->next) |
| 242 | if (f->size == size) return f; |
| 243 | f = new Fl_Font_Descriptor(s->name, size); |
| 244 | f->next = s->first; |
| 245 | s->first = f; |
| 246 | return f; |
| 247 | } |
| 248 | |
| 249 | //////////////////////////////////////////////////////////////// |
| 250 | // Public interface: |
| 251 | |
| 252 | void Fl_Quartz_Graphics_Driver::font(Fl_Font fnum, Fl_Fontsize size) { |
| 253 | if (fnum==-1) { |
| 254 | Fl_Graphics_Driver::font(0, 0); |
| 255 | return; |
| 256 | } |
| 257 | Fl_Graphics_Driver::font(fnum, size); |
| 258 | this->font_descriptor( find(fnum, size) ); |
| 259 | } |
| 260 | |
| 261 | int Fl_Quartz_Graphics_Driver::height() { |
| 262 | if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); |
| 263 | Fl_Font_Descriptor *fl_fontsize = font_descriptor(); |
| 264 | return fl_fontsize->ascent + fl_fontsize->descent; |
| 265 | } |
| 266 | |
| 267 | int Fl_Quartz_Graphics_Driver::descent() { |
| 268 | if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); |
| 269 | Fl_Font_Descriptor *fl_fontsize = font_descriptor(); |
| 270 | return fl_fontsize->descent+1; |
| 271 | } |
| 272 | |
| 273 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 274 | // returns width of a pair of UniChar's in the surrogate range |
| 275 | static CGFloat surrogate_width(const UniChar *txt, Fl_Font_Descriptor *fl_fontsize) |
| 276 | { |
| 277 | CTFontRef font2 = fl_fontsize->fontref; |
| 278 | bool must_release = false; |
| 279 | CGGlyph glyphs[2]; |
| 280 | bool b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); |
| 281 | CGSize a; |
| 282 | if(!b) { // the current font doesn't contain this char |
| 283 | CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, txt, 2, kCFAllocatorNull); |
| 284 | // find a font that contains it |
| 285 | font2 = CTFontCreateForString(font2, str, CFRangeMake(0,2)); |
| 286 | must_release = true; |
| 287 | CFRelease(str); |
| 288 | b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); |
| 289 | } |
| 290 | if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontHorizontalOrientation, glyphs, &a, 1); |
| 291 | else a.width = fl_fontsize->q_width; |
| 292 | if(must_release) CFRelease(font2); |
| 293 | return a.width; |
| 294 | } |
| 295 | #endif |
| 296 | |
| 297 | static double fl_mac_width(const UniChar* txt, int n, Fl_Font_Descriptor *fl_fontsize) { |
| 298 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 299 | if (fl_mac_os_version >= 100500) { |
| 300 | double retval = 0; |
| 301 | UniChar uni; |
| 302 | int i; |
| 303 | for (i = 0; i < n; i++) { // loop over txt |
| 304 | uni = txt[i]; |
| 305 | if (uni >= 0xD800 && uni <= 0xDBFF) { // handles the surrogate range |
| 306 | retval += surrogate_width(&txt[i], fl_fontsize); |
| 307 | i++; // because a pair of UniChar's represent a single character |
| 308 | continue; |
| 309 | } |
| 310 | const int block = 0x10000 / (sizeof(fl_fontsize->width)/sizeof(float*)); // block size |
| 311 | // r: index of the character block containing uni |
| 312 | unsigned int r = uni >> 7; // change 7 if sizeof(width) is changed |
| 313 | if (!fl_fontsize->width[r]) { // this character block has not been hit yet |
| 314 | //fprintf(stderr,"r=%d size=%d name=%s\n",r,fl_fontsize->size, fl_fontsize->q_name); |
| 315 | // allocate memory to hold width of each character in the block |
| 316 | fl_fontsize->width[r] = (float*) malloc(sizeof(float) * block); |
| 317 | UniChar ii = r * block; |
| 318 | CGSize advance_size; |
| 319 | CGGlyph glyph; |
| 320 | for (int j = 0; j < block; j++) { // loop over the block |
| 321 | CTFontRef font2 = fl_fontsize->fontref; |
| 322 | bool must_release = false; |
| 323 | // ii spans all characters of this block |
| 324 | bool b = CTFontGetGlyphsForCharacters(font2, &ii, &glyph, 1); |
| 325 | if (!b) { // the current font doesn't contain this char |
| 326 | CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, &ii, 1, kCFAllocatorNull); |
| 327 | // find a font that contains it |
| 328 | font2 = CTFontCreateForString(font2, str, CFRangeMake(0,1)); |
| 329 | must_release = true; |
| 330 | CFRelease(str); |
| 331 | b = CTFontGetGlyphsForCharacters(font2, &ii, &glyph, 1); |
| 332 | } |
| 333 | if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontHorizontalOrientation, &glyph, &advance_size, 1); |
| 334 | else advance_size.width = 0.; |
| 335 | // the width of one character of this block of characters |
| 336 | fl_fontsize->width[r][j] = advance_size.width; |
| 337 | if (must_release) CFRelease(font2); |
| 338 | ii++; |
| 339 | } |
| 340 | } |
| 341 | // sum the widths of all characters of txt |
| 342 | retval += fl_fontsize->width[r][uni & (block-1)]; |
| 343 | } |
| 344 | return retval; |
| 345 | } else { |
| 346 | #endif |
| 347 | #if ! __LP64__ |
| 348 | OSStatus err; |
| 349 | Fixed bBefore, bAfter, bAscent, bDescent; |
| 350 | ATSUTextLayout layout; |
| 351 | ByteCount iSize; |
| 352 | ATSUAttributeTag iTag; |
| 353 | ATSUAttributeValuePtr iValuePtr; |
| 354 | |
| 355 | // Here's my ATSU text measuring attempt... This seems to do the Right Thing |
| 356 | // now collect our ATSU resources and measure our text string |
| 357 | layout = fl_fontsize->layout; |
| 358 | // activate the current GC |
| 359 | iSize = sizeof(CGContextRef); |
| 360 | iTag = kATSUCGContextTag; |
| 361 | iValuePtr = &fl_gc; |
| 362 | ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); |
| 363 | // now measure the bounding box |
| 364 | err = ATSUSetTextPointerLocation(layout, txt, kATSUFromTextBeginning, n, n); |
| 365 | err = ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, n, &bBefore, &bAfter, &bAscent, &bDescent); |
| 366 | // If err is OK then return length, else return 0. Or something... |
| 367 | int len = FixedToInt(bAfter); |
| 368 | return len; |
| 369 | #endif |
| 370 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 371 | } |
| 372 | #endif |
| 373 | return 0; |
| 374 | } |
| 375 | |
| 376 | double Fl_Quartz_Graphics_Driver::width(const char* txt, int n) { |
| 377 | int wc_len = n; |
| 378 | UniChar *uniStr = mac_Utf8_to_Utf16(txt, n, &wc_len); |
| 379 | if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); |
| 380 | return fl_mac_width(uniStr, wc_len, font_descriptor()); |
| 381 | } |
| 382 | |
| 383 | double Fl_Quartz_Graphics_Driver::width(unsigned int wc) { |
| 384 | if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); |
| 385 | |
| 386 | UniChar utf16[3]; |
| 387 | int l = 1; |
| 388 | if (wc <= 0xFFFF) { |
| 389 | *utf16 = wc; |
| 390 | } |
| 391 | else { |
| 392 | // char buf[4]; |
| 393 | // l = fl_utf8encode(wc, buf); |
| 394 | // l = (int)fl_utf8toUtf16(buf, l, utf16, 3); |
| 395 | l = (int)fl_ucs_to_Utf16(wc, utf16, 3); |
| 396 | } |
| 397 | return fl_mac_width(utf16, l, font_descriptor()); |
| 398 | } |
| 399 | |
| 400 | // text extent calculation |
| 401 | void Fl_Quartz_Graphics_Driver::text_extents(const char *str8, int n, int &dx, int &dy, int &w, int &h) { |
| 402 | if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); |
| 403 | Fl_Font_Descriptor *fl_fontsize = font_descriptor(); |
| 404 | UniChar *txt = mac_Utf8_to_Utf16(str8, n, &n); |
| 405 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 406 | if (fl_mac_os_version >= 100500) { |
| 407 | CFStringRef str16 = CFStringCreateWithCharactersNoCopy(NULL, txt, n, kCFAllocatorNull); |
| 408 | CFDictionarySetValue (attributes, kCTFontAttributeName, fl_fontsize->fontref); |
| 409 | CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); |
| 410 | CFRelease(str16); |
| 411 | CTLineRef ctline = CTLineCreateWithAttributedString(mastr); |
| 412 | CFRelease(mastr); |
| 413 | CGContextSetTextPosition(fl_gc, 0, 0); |
| 414 | CGContextSetShouldAntialias(fl_gc, true); |
| 415 | CGRect rect = CTLineGetImageBounds(ctline, fl_gc); |
| 416 | CGContextSetShouldAntialias(fl_gc, false); |
| 417 | CFRelease(ctline); |
| 418 | dx = floor(rect.origin.x + 0.5); |
| 419 | dy = floor(- rect.origin.y - rect.size.height + 0.5); |
| 420 | w = rect.size.width + 0.5; |
| 421 | h = rect.size.height + 0.5; |
| 422 | } |
| 423 | else { |
| 424 | #endif |
| 425 | #if ! __LP64__ |
| 426 | OSStatus err; |
| 427 | ATSUTextLayout layout; |
| 428 | ByteCount iSize; |
| 429 | ATSUAttributeTag iTag; |
| 430 | ATSUAttributeValuePtr iValuePtr; |
| 431 | |
| 432 | // Here's my ATSU text measuring attempt... This seems to do the Right Thing |
| 433 | // now collect our ATSU resources and measure our text string |
| 434 | layout = fl_fontsize->layout; |
| 435 | // activate the current GC |
| 436 | iSize = sizeof(CGContextRef); |
| 437 | iTag = kATSUCGContextTag; |
| 438 | iValuePtr = &fl_gc; |
| 439 | ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); |
| 440 | // now measure the bounding box |
| 441 | err = ATSUSetTextPointerLocation(layout, txt, kATSUFromTextBeginning, n, n); |
| 442 | Rect bbox; |
| 443 | err = ATSUMeasureTextImage(layout, kATSUFromTextBeginning, n, 0, 0, &bbox); |
| 444 | w = bbox.right - bbox.left; |
| 445 | h = bbox.bottom - bbox.top; |
| 446 | dx = bbox.left; |
| 447 | dy = -bbox.bottom; |
| 448 | //printf("r: %d l: %d t: %d b: %d w: %d h: %d\n", bbox.right, bbox.left, bbox.top, bbox.bottom, w, h); |
| 449 | #endif |
| 450 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 451 | } |
| 452 | #endif |
| 453 | return; |
| 454 | } // fl_text_extents |
| 455 | |
| 456 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 457 | static CGColorRef flcolortocgcolor(Fl_Color i) |
| 458 | { |
| 459 | uchar r, g, b; |
| 460 | Fl::get_color(i, r, g, b); |
| 461 | CGFloat components[4] = {r/255.0f, g/255.0f, b/255.0f, 1.}; |
| 462 | static CGColorSpaceRef cspace = NULL; |
| 463 | if (cspace == NULL) { |
| 464 | cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); |
| 465 | } |
| 466 | return CGColorCreate(cspace, components); |
| 467 | } |
| 468 | #endif |
| 469 | |
| 470 | static void fl_mac_draw(const char *str, int n, float x, float y, Fl_Graphics_Driver *driver) { |
| 471 | // convert to UTF-16 first |
| 472 | UniChar *uniStr = mac_Utf8_to_Utf16(str, n, &n); |
| 473 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 474 | if (fl_mac_os_version >= 100500) { |
| 475 | CFStringRef str16 = CFStringCreateWithCharactersNoCopy(NULL, uniStr, n, kCFAllocatorNull); |
| 476 | if (str16 == NULL) return; // shd not happen |
| 477 | CGColorRef color = flcolortocgcolor(driver->color()); |
| 478 | CFDictionarySetValue (attributes, kCTFontAttributeName, driver->font_descriptor()->fontref); |
| 479 | CFDictionarySetValue (attributes, kCTForegroundColorAttributeName, color); |
| 480 | CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); |
| 481 | CFRelease(str16); |
| 482 | CFRelease(color); |
| 483 | CTLineRef ctline = CTLineCreateWithAttributedString(mastr); |
| 484 | CFRelease(mastr); |
| 485 | CGContextSetTextMatrix(fl_gc, font_mx); |
| 486 | CGContextSetTextPosition(fl_gc, x, y); |
| 487 | CGContextSetShouldAntialias(fl_gc, true); |
| 488 | CTLineDraw(ctline, fl_gc); |
| 489 | CGContextSetShouldAntialias(fl_gc, false); |
| 490 | CFRelease(ctline); |
| 491 | } else { |
| 492 | #endif |
| 493 | #if ! __LP64__ |
| 494 | OSStatus err; |
| 495 | // now collect our ATSU resources |
| 496 | ATSUTextLayout layout = driver->font_descriptor()->layout; |
| 497 | |
| 498 | ByteCount iSize = sizeof(CGContextRef); |
| 499 | ATSUAttributeTag iTag = kATSUCGContextTag; |
| 500 | ATSUAttributeValuePtr iValuePtr=&fl_gc; |
| 501 | ATSUSetLayoutControls(layout, 1, &iTag, &iSize, &iValuePtr); |
| 502 | |
| 503 | err = ATSUSetTextPointerLocation(layout, uniStr, kATSUFromTextBeginning, n, n); |
| 504 | CGContextSetShouldAntialias(fl_gc, true); |
| 505 | err = ATSUDrawText(layout, kATSUFromTextBeginning, n, FloatToFixed(x), FloatToFixed(y)); |
| 506 | CGContextSetShouldAntialias(fl_gc, false); |
| 507 | #endif |
| 508 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
| 509 | } |
| 510 | #endif |
| 511 | } |
| 512 | |
| 513 | void Fl_Quartz_Graphics_Driver::draw(const char *str, int n, float x, float y) { |
| 514 | // avoid a crash if no font has been selected by user yet ! |
| 515 | if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); |
| 516 | fl_mac_draw(str, n, x, y, this); |
| 517 | } |
| 518 | |
| 519 | void Fl_Quartz_Graphics_Driver::draw(const char* str, int n, int x, int y) { |
| 520 | // avoid a crash if no font has been selected by user yet ! |
| 521 | if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); |
| 522 | fl_mac_draw(str, n, (float)x-0.0f, (float)y+0.5f, this); |
| 523 | } |
| 524 | |
| 525 | void Fl_Quartz_Graphics_Driver::draw(int angle, const char *str, int n, int x, int y) { |
| 526 | CGContextSaveGState(fl_gc); |
| 527 | CGContextTranslateCTM(fl_gc, x, y); |
| 528 | CGContextRotateCTM(fl_gc, - angle*(M_PI/180) ); |
| 529 | draw(str, n, 0, 0); |
| 530 | CGContextRestoreGState(fl_gc); |
| 531 | } |
| 532 | |
| 533 | void Fl_Quartz_Graphics_Driver::rtl_draw(const char* c, int n, int x, int y) { |
| 534 | int dx, dy, w, h; |
| 535 | text_extents(c, n, dx, dy, w, h); |
| 536 | draw(c, n, x - w - dx, y); |
| 537 | } |
| 538 | |
| 539 | // |
| 540 | // End of "$Id: fl_font_mac.cxx 8597 2011-04-17 13:18:55Z ianmacarthur $". |
| 541 | // |