1 /*
2  *  A Z-Machine
3  *  Copyright (C) 2000 Andrew Hunter
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 /*
21  * Fonts for Mac OS (Carbon)
22  *
23  * There are no less than three different font drivers here:
24  *   - QuickDraw Text
25  *   - ATSUI
26  *   - Quartz
27  *
28  * ATSUI is slow, and to use it properly you'd really need to rewrite
29  * the whole rendering interface. That's probably not worth it...
30  * (Well, it would make copy/paste a bit easier to implement
31  *
32  * QuickDraw uses a crappy font rendering engine
33  *
34  * Quartz looks nice, but there is a shortage of ways of measuring text:
35  * the way we do it is a bit of a hack, but seems accurate enough.
36  * (You can't define USE_QUARTZ and USE_ATS...)
37  *
38  * All this is why this file is a bit of a mess in places...
39  */
40 
41 #include "../config.h"
42 
43 #if WINDOW_SYSTEM == 3
44 
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include <Carbon/Carbon.h>
49 
50 #include "zmachine.h"
51 #include "rc.h"
52 #include "font3.h"
53 #include "xfont.h"
54 #include "carbondisplay.h"
55 
56 extern XFONT_MEASURE xfont_x;
57 extern XFONT_MEASURE xfont_y;
58 
59 static PolyHandle f3[96];
60 
61 struct xfont
62 {
63   enum
64     {
65       FONT_INTERNAL,
66       FONT_FONT3
67     } type;
68   union
69   {
70     struct
71     {
72 #ifdef USE_ATS
73       FMFontFamily family;
74       ATSUFontID font;
75       ATSUStyle  style;
76 #else
77       FMFontFamily family;
78       int size;
79       int isbold;
80       int isitalic;
81       int isunderlined;
82 
83       TextEncoding      encoding;
84       UnicodeToTextInfo convert;
85 
86 # ifdef USE_QUARTZ
87       ATSFontRef atsref;
88       ATSUStyle  style;
89       char*      psname;
90       CGFontRef  cgfont;
91 # endif
92 
93 #endif
94 
95       XFONT_MEASURE ascent, descent, maxwidth;
96     } mac;
97   } data;
98 };
99 
100 static RGBColor fg_col, bg_col;
101 static int transpar = 0;
102 
103 #ifdef USE_QUARTZ
104 CGContextRef carbon_quartz_context = nil;
105 
106 static xfont*       winlastfont = NULL;
107 static int          enable_quartz = 0;
108 #endif
109 
110 /***                           ----// 888 \\----                           ***/
111 
112 #ifdef USE_QUARTZ
carbon_set_context(void)113 void carbon_set_context(void)
114 {
115   CGrafPtr thePort = GetQDGlobalsThePort();
116 
117   CGContextRelease(carbon_quartz_context);
118   CreateCGContextForPort(thePort, &carbon_quartz_context);
119   winlastfont = NULL;
120 
121   if (rc_get_antialias())
122     {
123       CGContextSetShouldAntialias(carbon_quartz_context,
124 				  1);
125     }
126   else
127     {
128       CGContextSetShouldAntialias(carbon_quartz_context,
129 				  0);
130     }
131 }
132 
carbon_set_quartz(int q)133 void carbon_set_quartz(int q)
134 {
135   enable_quartz = q;
136 }
137 #endif
138 
139 static double scale_factor = 1.0;
140 
carbon_set_scale_factor(double factor)141 void carbon_set_scale_factor(double factor)
142 {
143   scale_factor = factor;
144 }
145 
xfont_initialise(void)146 void xfont_initialise(void)
147 {
148   int x;
149 
150 #ifdef USE_QUARTZ
151   if (carbon_quartz_context == nil)
152     {
153       CGrafPtr p;
154       OSStatus res;
155 
156       p = GetWindowPort(zoomWindow);
157 
158       res = CreateCGContextForPort(p, &carbon_quartz_context);
159 
160       winlastfont = NULL;
161     }
162 #endif
163 
164   for (x=0; x<96; x++)
165     f3[x] = nil;
166 }
167 
xfont_shutdown(void)168 void xfont_shutdown(void)
169 {
170 }
171 
172 #ifndef USE_ATS
select_font(xfont * font)173 static void select_font(xfont* font)
174 {
175   TextFont(font->data.mac.family);
176   TextSize(font->data.mac.size);
177   TextFace((font->data.mac.isbold?bold:0)           |
178 	   (font->data.mac.isitalic?italic:0)       |
179 	   (font->data.mac.isunderlined?underline:0));
180 }
181 #endif
182 
183 #define DEFAULT_FONT applFont
184 
185 
186 /*
187  * Internal format for Mac OS font names
188  *
189  * "face name" width properties
190  *
191  * Where properties can be one or more of:
192  *   b - bold
193  *   i - italic
194  *   u - underline
195  */
xfont_default_font(void)196 static xfont* xfont_default_font(void)
197 {
198   xfont* xf;
199 
200 #ifdef USE_ATS
201   return NULL;
202 #else
203   xf = malloc(sizeof(struct xfont));
204   xf->type = FONT_INTERNAL;
205   xf->data.mac.family       = DEFAULT_FONT;
206   xf->data.mac.size         = 12;
207   xf->data.mac.isbold       = 0;
208   xf->data.mac.isitalic     = 0;
209   xf->data.mac.isunderlined = 0;
210   return xf;
211 #endif
212 }
213 
carbon_parse_font(char * font)214 carbon_font* carbon_parse_font(char* font)
215 {
216   int x;
217 
218   static carbon_font fnt;
219   char*       face_name;
220   static char fontcopy[256];
221   char*       face_width;
222   char*       face_props;
223 
224   if (strcmp(font, "font3") == 0)
225     {
226       fnt.isfont3 = 1;
227       fnt.isbold = fnt.isitalic = fnt.isunderlined = 0;
228       fnt.size = 0;
229       return &fnt;
230     }
231   fnt.isfont3 = 0;
232 
233   if (strlen(font) > 256)
234     {
235       zmachine_warning("Invalid font name (too long)");
236 
237       return NULL;
238     }
239 
240   /* Get the face name */
241   strcpy(fontcopy, font);
242   x = 0;
243   while (fontcopy[x++] != '\'')
244     {
245       if (fontcopy[x] == 0)
246 	{
247 	  zmachine_warning("Invalid font name: %s (font name must be in single quotes)", font);
248 
249 	  return NULL;
250 	}
251     }
252 
253   face_name = &fontcopy[x];
254 
255   x--;
256   while (fontcopy[++x] != '\'')
257     {
258       if (fontcopy[x] == 0)
259 	{
260 	  zmachine_warning("Invalid font name: %s (missing \')", font);
261 
262 	  return NULL;
263 	}
264     }
265   fontcopy[x] = 0;
266 
267   /* Get the font width */
268   while (fontcopy[++x] == ' ')
269     {
270       if (fontcopy[x] == 0)
271 	{
272 	  zmachine_warning("Invalid font name: %s (no font size specified)", font);
273 
274 	  return NULL;
275 	}
276     }
277 
278   face_width = &fontcopy[x];
279 
280   while (fontcopy[x] >= '0' &&
281 	 fontcopy[x] <= '9')
282     x++;
283 
284   if (fontcopy[x] != ' ' &&
285       fontcopy[x] != 0)
286     {
287       zmachine_warning("Invalid font name: %s (invalid size)", font);
288 
289       return NULL;
290     }
291 
292   if (fontcopy[x] != 0)
293     {
294       fontcopy[x] = 0;
295       face_props  = &fontcopy[x+1];
296     }
297   else
298     face_props = NULL;
299 
300   fnt.face_name = face_name;
301   fnt.size = atoi(face_width);
302   fnt.isbold = fnt.isitalic = fnt.isunderlined = 0;
303 
304   if (face_props != NULL)
305     {
306       for (x=0; face_props[x] != 0; x++)
307 	{
308 	  switch (face_props[x])
309 	    {
310 	    case 'b':
311 	    case 'B':
312 	      fnt.isbold = 1;
313 	      break;
314 
315 	    case 'i':
316 	    case 'I':
317 	      fnt.isitalic = 1;
318 	      break;
319 
320 	    case 'u':
321 	    case 'U':
322 	      fnt.isunderlined = 1;
323 	      break;
324 	    }
325 	}
326     }
327 
328   return &fnt;
329 }
330 
xfont_load_font(char * font)331 xfont* xfont_load_font(char* font)
332 {
333   char   fontcopy[256];
334   char*  face_name;
335   Str255 family;
336   char*  face_width;
337   char*  face_props;
338   xfont* xf;
339 
340   int x;
341 
342   GrafPtr oldport;
343   FontInfo fm;
344   int aspace[] = { 'T', 'e', 's', 't' };
345 
346 #ifdef USE_ATS
347   OSStatus erm;
348 
349   ATSUAttributeTag tags[3] =
350     {
351       kATSUSizeTag, kATSUQDUnderlineTag,
352       kATSUFontTag
353     };
354   ByteCount             attsz [3];
355   ATSUAttributeValuePtr attptr[3];
356 
357   Fixed size;
358   Boolean isbold, isitalic, isunderline;
359 #endif
360 
361   if (strcmp(font, "font3") == 0)
362     {
363       xf = malloc(sizeof(struct xfont));
364 
365       xf->type = FONT_FONT3;
366 
367       return xf;
368     }
369 
370   if (strlen(font) > 256)
371     {
372       zmachine_warning("Invalid font name (too long)");
373 
374       return xfont_default_font();
375     }
376 
377   /* Get the face name */
378   strcpy(fontcopy, font);
379   x = 0;
380   while (fontcopy[x++] != '\'')
381     {
382       if (fontcopy[x] == 0)
383 	{
384 	  zmachine_warning("Invalid font name: %s (font name must be in single quotes)", font);
385 
386 	  xf = xfont_default_font();
387 	  return xf;
388 	}
389     }
390 
391   face_name = &fontcopy[x];
392 
393   x--;
394   while (fontcopy[++x] != '\'')
395     {
396       if (fontcopy[x] == 0)
397 	{
398 	  zmachine_warning("Invalid font name: %s (missing \')", font);
399 
400 	  xf = xfont_default_font();
401 	  return xf;
402 	}
403     }
404   fontcopy[x] = 0;
405 
406   /* Get the font width */
407   while (fontcopy[++x] == ' ')
408     {
409       if (fontcopy[x] == 0)
410 	{
411 	  zmachine_warning("Invalid font name: %s (no font size specified)", font);
412 
413 	  xf = xfont_default_font();
414 	  return xf;
415 	}
416     }
417 
418   face_width = &fontcopy[x];
419 
420   while (fontcopy[x] >= '0' &&
421 	 fontcopy[x] <= '9')
422     x++;
423 
424   if (fontcopy[x] != ' ' &&
425       fontcopy[x] != 0)
426     {
427       zmachine_warning("Invalid font name: %s (invalid size)", font);
428 
429       xf = xfont_default_font();
430       return xf;
431     }
432 
433   if (fontcopy[x] != 0)
434     {
435       fontcopy[x] = 0;
436       face_props  = &fontcopy[x+1];
437     }
438   else
439     face_props = NULL;
440 
441   xf = malloc(sizeof(xfont));
442 
443 #ifdef USE_ATS
444   /* Locate the font */
445   xf->type = FONT_INTERNAL;
446   isbold = isitalic = isunderline = false;
447 
448   if (face_props != NULL)
449     {
450       for (x=0; face_props[x] != 0; x++)
451 	{
452 	  switch (face_props[x])
453 	    {
454 	    case 'b':
455 	    case 'B':
456 	      isbold = true;
457 	      break;
458 
459 	    case 'i':
460 	    case 'I':
461 	      isitalic = true;
462 	      break;
463 
464 	    case 'u':
465 	    case 'U':
466 	      isunderline = true;
467 	      break;
468 	    }
469 	}
470     }
471 
472   family[0] = strlen(face_name);
473   strcpy(family+1, face_name);
474   xf->data.mac.family = FMGetFontFamilyFromName(family);
475   erm = FMGetFontFromFontFamilyInstance(xf->data.mac.family,
476 					(isbold?bold:0)           |
477 					(isitalic?italic:0)       |
478 					(isunderline?underline:0),
479 					&xf->data.mac.font,
480 					NULL);
481 
482   if (erm != noErr)
483     {
484       zmachine_warning("Font family '%s' not found", face_name);
485       free(xf);
486       return xfont_default_font();
487     }
488 
489   ATSUCreateStyle(&xf->data.mac.style);
490 
491   size = atoi(face_width)<<16;
492   size = (int) ((double)size * scale_factor);
493 
494   /* Set the attributes of this font */
495   attsz[0] = sizeof(Fixed);
496   attsz[1] = sizeof(Boolean);
497   attsz[2] = sizeof(ATSUFontID);
498   attptr[0] = &size;
499   attptr[1] = &isunderline;
500   attptr[2] = &xf->data.mac.font;
501 
502   ATSUSetAttributes(xf->data.mac.style, 3, tags, attsz, attptr);
503 
504   /* Measure the font */
505   {
506     ATSUTextLayout lo;
507     UniChar text[1] = { 'M' };
508 
509     ATSUTextMeasurement before, after, ascent, descent;
510 
511     /*
512      * (Sigh, there has to be a better way of doing things... We really just
513      * want the metrics)
514      */
515 
516     ATSUCreateTextLayout(&lo);
517     ATSUSetTextPointerLocation(lo, text, 0, 1, 1);
518     ATSUSetRunStyle(lo, xf->data.mac.style, 0, 1);
519     ATSUMeasureText(lo, 0, 1, &before, &after, &ascent, &descent);
520 
521     xf->data.mac.ascent = ascent/65536.0;
522     xf->data.mac.descent = descent/65536.0;
523     xf->data.mac.maxwidth = (after+before)/65536.0;
524 
525     ATSUDisposeTextLayout(lo);
526   }
527 #else
528 
529   xf->type = FONT_INTERNAL;
530   family[0] = strlen(face_name);
531   strcpy(family+1, face_name);
532   xf->data.mac.family = FMGetFontFamilyFromName(family);
533   if (xf->data.mac.family == kInvalidFontFamily)
534     {
535       zmachine_warning("Font '%s' not found, reverting to default", face_name);
536       xf->data.mac.family = DEFAULT_FONT;
537     }
538   xf->data.mac.size = (int)((double)atoi(face_width)*scale_factor);
539   xf->data.mac.isbold = 0;
540   xf->data.mac.isitalic = 0;
541   xf->data.mac.isunderlined = 0;
542 
543   if (face_props != NULL)
544     {
545       for (x=0; face_props[x] != 0; x++)
546 	{
547 	  switch (face_props[x])
548 	    {
549 	    case 'b':
550 	    case 'B':
551 	      xf->data.mac.isbold = 1;
552 	      break;
553 
554 	    case 'i':
555 	    case 'I':
556 	      xf->data.mac.isitalic = 1;
557 	      break;
558 
559 	    case 'u':
560 	    case 'U':
561 	      xf->data.mac.isunderlined = 1;
562 	      break;
563 	    }
564 	}
565     }
566 
567   if (FMGetFontFamilyTextEncoding(xf->data.mac.family, &xf->data.mac.encoding)
568       != noErr)
569     zmachine_fatal("Unable to get encoding for font '%s'", face_name);
570   if (CreateUnicodeToTextInfoByEncoding(xf->data.mac.encoding, &xf->data.mac.convert)
571       != noErr)
572     zmachine_fatal("Unable to create TextInfo structure for font '%s'", face_name);
573 
574   GetPort(&oldport);
575   SetPort(GetWindowPort(zoomWindow));
576 
577   select_font(xf);
578   GetFontInfo(&fm);
579 
580 # ifdef USE_QUARTZ
581   {
582     FMFont font;
583     OSStatus erm;
584 
585     erm = FMGetFontFromFontFamilyInstance(xf->data.mac.family,
586 					  (xf->data.mac.isbold?bold:0)           |
587 					  (xf->data.mac.isitalic?italic:0)       |
588 					  (xf->data.mac.isunderlined?underline:0),
589 					  &font,
590 					  NULL);
591 
592     if (erm != noErr)
593       zmachine_fatal("Unable to get FMFont structure for font '%s'", face_name);
594 
595     xf->data.mac.atsref = FMGetATSFontRefFromFont(font);
596     xf->data.mac.cgfont = CGFontCreateWithPlatformFont(&xf->data.mac.atsref);
597     {
598       CFStringRef str;
599       char buf[256];
600 
601       ATSUAttributeTag tags[5] =
602 	{
603 	  kATSUSizeTag, kATSUQDBoldfaceTag,
604 	  kATSUQDItalicTag, kATSUQDUnderlineTag,
605 	  kATSUFontTag
606 	};
607       ByteCount             attsz [5];
608       ATSUAttributeValuePtr attptr[5];
609 
610       ATSUFontFeatureType fe_types[] = { kLigaturesType };
611       ATSUFontFeatureSelector fe_sel[] = { kCommonLigaturesOffSelector };
612 
613       /* Get the name of this font */
614       ATSFontGetPostScriptName(xf->data.mac.atsref,
615 			       0,
616 			       &str);
617       CFStringGetCString(str, buf, 256, kCFStringEncodingMacRoman);
618 
619       xf->data.mac.psname = malloc(strlen(buf)+1);
620       strcpy(xf->data.mac.psname, buf);
621       CFRelease(str);
622 
623       /*
624        * Create an ATSU style (bleh, we need to do this so we can work out
625        * the glyph IDs to plot)
626        */
627       ATSUCreateStyle(&xf->data.mac.style);
628 
629       attsz[1] = attsz[2] = attsz[3] = sizeof(Boolean);
630       attsz[4] = sizeof(ATSUFontID);
631       attptr[0] = &xf->data.mac.size;
632       attptr[1] = &xf->data.mac.isbold;
633       attptr[2] = &xf->data.mac.isitalic;
634       attptr[3] = &xf->data.mac.isunderlined;
635       attptr[4] = &xf->data.mac.atsref;
636 
637       ATSUSetAttributes(xf->data.mac.style, 4, tags+1, attsz+1, attptr+1);
638 
639       ATSUSetFontFeatures(xf->data.mac.style, 1, fe_types, fe_sel);
640     }
641   }
642 # endif
643 
644   xf->data.mac.ascent   = fm.ascent;
645   xf->data.mac.descent  = fm.descent + fm.leading;
646   xf->data.mac.maxwidth = xfont_get_text_width(xf, aspace, 4)/4.0;
647 
648   SetPort(oldport);
649 #endif
650 
651   return xf;
652 }
653 
xfont_release_font(xfont * xf)654 void xfont_release_font(xfont* xf)
655 {
656   if (xf->type != FONT_FONT3)
657     {
658 #ifdef USE_ATS
659       ATSUDisposeStyle(xf->data.mac.style);
660 #else
661       DisposeUnicodeToTextInfo(&xf->data.mac.convert);
662 # ifdef USE_QUARTZ
663       CGFontRelease(xf->data.mac.cgfont);
664       ATSUDisposeStyle(xf->data.mac.style);
665       free(xf->data.mac.psname);
666 # endif
667 #endif
668     }
669   free(xf);
670 }
671 
xfont_set_colours(int fg,int bg)672 void xfont_set_colours(int fg, int bg)
673 {
674   fg_col = *carbon_get_colour(fg);
675 
676   transpar = 0;
677   if (bg >= 0)
678     bg_col = *carbon_get_colour(bg);
679   else
680     transpar = 1;
681 }
682 
xfont_get_height(xfont * xf)683 XFONT_MEASURE xfont_get_height(xfont* xf)
684 {
685   if (xf->type == FONT_FONT3)
686     return xfont_y;
687 
688   return xf->data.mac.ascent + xf->data.mac.descent;
689 }
690 
xfont_get_ascent(xfont * xf)691 XFONT_MEASURE xfont_get_ascent(xfont* xf)
692 {
693   if (xf->type == FONT_FONT3)
694     return xfont_y;
695 
696   return xf->data.mac.ascent;
697 }
698 
xfont_get_descent(xfont * xf)699 XFONT_MEASURE xfont_get_descent(xfont* xf)
700 {
701   if (xf->type == FONT_FONT3)
702     return 0;
703 
704   return xf->data.mac.descent;
705 }
706 
xfont_get_width(xfont * xf)707 XFONT_MEASURE xfont_get_width(xfont* xf)
708 {
709   if (xf->type == FONT_FONT3)
710     return xfont_x;
711 
712   return xf->data.mac.maxwidth;
713 }
714 
715 #ifndef USE_ATS
convert_text(xfont * font,const int * string,int length,ByteCount * olen)716 static char* convert_text(xfont* font,
717 			  const int* string,
718 			  int length,
719 			  ByteCount* olen)
720 {
721   static UniChar* iunicode = NULL;
722   static char*    outbuf   = NULL;
723   static int      warned   = 0;
724 
725   int  z;
726 
727   ByteCount inread;
728   ByteCount outlen;
729 
730   OSStatus res;
731 
732   iunicode = realloc(iunicode, sizeof(UniChar)*length);
733   for (z=0; z < length; z++)
734     {
735       iunicode[z] = string[z];
736     }
737 
738   outbuf = realloc(outbuf, length*2);
739 
740   do
741     {
742       res = ConvertFromUnicodeToText(font->data.mac.convert,
743 				     sizeof(UniChar)*length, iunicode,
744 				     kUnicodeLooseMappingsMask, 0,
745 				     NULL, NULL, NULL,
746 				     length*2, &inread, &outlen,
747 				     outbuf);
748 
749       if (res == kTECUnmappableElementErr)
750 	{
751 	  if (iunicode[inread>>1] == '?')
752 	    break;
753 	  iunicode[inread>>1] = '?';
754 	}
755     }
756   while (res == kTECUnmappableElementErr);
757 
758   if (res != noErr)
759     {
760       if (warned == 0)
761 	{
762 	  warned = 1;
763 	  zmachine_warning("Unable to convert game text to font text");
764 	}
765 
766       if (olen != NULL)
767 	(*olen) = 3;
768       return "<?>";
769     }
770 
771   if (olen != NULL)
772     (*olen) = outlen;
773 
774   return outbuf;
775 }
776 #endif
777 
778 #if 1
779 /*
780  * This requires some explaination...
781  *
782  * CGContextSelectFont() is slow. So we want to use CGContextSetFont,
783  * which is reasonably fast. However, that doesn't support *any* encoding
784  * conversions, so we need to use ATSU to get the glyph details of the
785  * text we're about to plot.
786  */
make_atsu_layout(xfont * font,const int * string,int length)787 static ATSUTextLayout make_atsu_layout(xfont* font,
788 					const int* string,
789 					int length)
790 {
791   ATSUTextLayout lay;
792 
793   static UniChar* str = NULL;
794   UniCharCount runlength[1];
795   ATSUStyle    style[1];
796 
797   int x;
798 
799   str = realloc(str, sizeof(UniChar)*length);
800   for (x=0; x<length; x++)
801     {
802       str[x] = string[x];
803     }
804 
805   runlength[0] = length;
806   style[0] = font->data.mac.style;
807   ATSUCreateTextLayoutWithTextPtr(str, 0, length, length, 1, runlength, style,
808 				  &lay);
809 
810   return lay;
811 }
812 
convert_glyphs(xfont * font,const int * string,int length,SInt32 * olen)813 static CGGlyph* convert_glyphs(xfont* font,
814 			       const int* string,
815 			       int length,
816 			       SInt32* olen)
817 {
818   static ATSUGlyphInfoArray* res = NULL;
819   static CGGlyph* out = NULL;
820 
821   ATSUTextLayout lay;
822 
823   int x;
824 
825   ByteCount    bufsize;
826 
827   if (length <= 0)
828     {
829       *olen = 0;
830       return NULL;
831     }
832 
833   lay = make_atsu_layout(font, string, length);
834 
835   if (ATSUGetGlyphInfo(lay, 0, length, &bufsize, NULL) != noErr)
836     {
837       *olen = 0;
838       return NULL;
839     }
840   res = realloc(res, bufsize);
841   ATSUGetGlyphInfo(lay, 0, length, &bufsize, res);
842 
843   ATSUDisposeTextLayout(lay);
844 
845   *olen = 0;
846 
847   out = realloc(out, sizeof(CGGlyph)*res->numGlyphs);
848   for (x=0; x<res->numGlyphs; x++)
849     {
850       if (res->glyphs[x].glyphID != 0xffff)
851 	out[(*olen)++] = res->glyphs[x].glyphID;
852     }
853 
854   return out;
855 }
856 #endif
857 
858 #ifdef USE_ATS
make_layout(xfont * xf,const int * string,int len,ATSUTextLayout lo)859 static void make_layout(xfont*         xf,
860 			const int*     string,
861 			int            len,
862 			ATSUTextLayout lo)
863 {
864   static UniChar* ustr = NULL;
865   int x;
866 
867   ustr = realloc(ustr, sizeof(UniChar)*len);
868   for (x=0; x<len; x++)
869     ustr[x] = string[x];
870 
871   ATSUSetTextPointerLocation(lo, ustr, 0, len, len);
872   ATSUSetRunStyle(lo, xf->data.mac.style, 0, len);
873 }
874 #endif
875 
xfont_get_text_width(xfont * xf,const int * string,int length)876 XFONT_MEASURE xfont_get_text_width(xfont* xf,
877 				   const int* string,
878 				   int length)
879 {
880 #ifdef USE_ATS
881   ATSUTextLayout lo;
882   GrafPtr oldport;
883 
884   static UniChar* str = NULL;
885   UniCharCount runlength[1];
886   ATSUStyle    style[1];
887 
888   int x;
889 
890   if (length <= 0)
891     return;
892 
893   ATSUTextMeasurement before, after, ascent, descent;
894 
895   if (xf->type == FONT_FONT3)
896     return length*xfont_x;
897 
898   GetPort(&oldport);
899   SetPort(GetWindowPort(zoomWindow));
900 
901   str = realloc(str, sizeof(UniChar)*length);
902   for (x=0; x<length; x++)
903     str[x] = string[x];
904 
905   runlength[0] = length;
906   style[0] = xf->data.mac.style;
907   ATSUCreateTextLayoutWithTextPtr(str, 0, length, length, 1, runlength, style,
908 				  &lo);
909 
910   ATSUMeasureText(lo, 0, length, &before, &after, &ascent, &descent);
911   ATSUDisposeTextLayout(lo);
912 
913   SetPort(oldport);
914 
915   return (XFONT_MEASURE)(after+before)/65536.0;
916 #else
917   GrafPtr oldport;
918 
919   char*     outbuf;
920   ByteCount outlen;
921   XFONT_MEASURE res;
922 
923   if (xf->type == FONT_FONT3)
924     return length*xfont_x;
925 
926 #ifdef USE_QUARTZ
927   if (!enable_quartz)
928     {
929 #endif
930       GetPort(&oldport);
931       SetPort(GetWindowPort(zoomWindow));
932 
933       select_font(xf);
934       outbuf = convert_text(xf, string, length, &outlen);
935       res = TextWidth(outbuf, 0, outlen);
936 
937       SetPort(oldport);
938 #ifdef USE_QUARTZ
939     }
940   else
941     {
942       CGPoint end;
943       CGGlyph* glyph;
944 
945       if (winlastfont != xf)
946 	{
947 	  CGContextSetFont(carbon_quartz_context, xf->data.mac.cgfont);
948 	  CGContextSetFontSize(carbon_quartz_context, xf->data.mac.size);
949 	  winlastfont = xf;
950 	}
951       glyph = convert_glyphs(xf, string, length, &outlen);
952       CGContextSetTextPosition(carbon_quartz_context, 0, 0);
953       CGContextSetTextDrawingMode(carbon_quartz_context, kCGTextInvisible);
954       CGContextShowGlyphs(carbon_quartz_context, glyph, outlen);
955 
956       end = CGContextGetTextPosition(carbon_quartz_context);
957 
958       res = end.x;
959     }
960 #endif
961 
962   return res;
963 #endif
964 }
965 
plot_font_3(int chr,XFONT_MEASURE xpos,XFONT_MEASURE ypos)966 static void plot_font_3(int chr, XFONT_MEASURE xpos, XFONT_MEASURE ypos)
967 {
968   int x;
969 
970   if (chr > 127 || chr < 32)
971     return;
972   chr-=32;
973 
974   if (font_3.chr[chr].num_coords < 0)
975     {
976       zmachine_warning("Attempt to plot unspecified character %i",
977 		       chr+32);
978       return;
979     }
980 
981 #ifdef USE_QUARTZ
982   if (enable_quartz)
983     {
984       CGContextBeginPath(carbon_quartz_context);
985       CGContextMoveToPoint(carbon_quartz_context,
986 			   (font_3.chr[chr].coords[0]*xfont_x) / 8.0 + xpos,
987 			   ((8-font_3.chr[chr].coords[1])*xfont_y) / 8.0 + ypos);
988       for (x=0; x<font_3.chr[chr].num_coords; x++)
989 	{
990 	  CGContextAddLineToPoint(carbon_quartz_context,
991 				  (font_3.chr[chr].coords[x<<1]*xfont_x) / 8.0 + xpos,
992 				  ((8-font_3.chr[chr].coords[(x<<1)+1])*xfont_y) / 8.0 + ypos);
993 	}
994       CGContextEOFillPath(carbon_quartz_context);
995     }
996   else
997 #endif
998     {
999       if (f3[chr] == nil)
1000 	{
1001 	  MoveTo((font_3.chr[chr].coords[0]*xfont_x) / 8.0 + 0.5,
1002 		 (font_3.chr[chr].coords[1]*xfont_y) / 8.0 + 0.5);
1003 	  f3[chr] = OpenPoly();
1004 	  for (x=0; x<font_3.chr[chr].num_coords; x++)
1005 	    {
1006 	      LineTo((font_3.chr[chr].coords[x<<1]*xfont_x) / 8.0 + 0.5,
1007 		     (font_3.chr[chr].coords[(x<<1)+1]*xfont_y) / 8.0 + 0.5);
1008 	    }
1009 	  ClosePoly();
1010 	}
1011       OffsetPoly(f3[chr], xpos, ypos);
1012       PaintPoly(f3[chr]);
1013       OffsetPoly(f3[chr], -xpos, -ypos);
1014     }
1015 }
1016 
xfont_plot_string(xfont * font,XFONT_MEASURE x,XFONT_MEASURE y,const int * string,int length)1017 void xfont_plot_string(xfont* font,
1018 		       XFONT_MEASURE x, XFONT_MEASURE y,
1019 		       const int* string,
1020 		       int length)
1021 {
1022   char*     outbuf;
1023   ByteCount outlen;
1024 
1025   Rect portRect;
1026 
1027   CGrafPtr thePort = GetQDGlobalsThePort();
1028 
1029   if (length <= 0)
1030     return;
1031 
1032   GetPortBounds(thePort, &portRect);
1033 
1034   if (font->type == FONT_FONT3)
1035     {
1036       int pos;
1037 
1038 #ifdef USE_QUARTZ
1039       if (enable_quartz)
1040 	{
1041 	  CGContextSetRGBFillColor(carbon_quartz_context,
1042 				   (float)fg_col.red/65536.0,
1043 				   (float)fg_col.green/65536.0,
1044 				   (float)fg_col.blue/65536.0,
1045 				   1.0);
1046 
1047 	  for (pos = 0; pos<length; pos++)
1048 	    {
1049 	      plot_font_3(string[pos],
1050 			  portRect.left + x + xfont_x*pos,
1051 			  (portRect.bottom-portRect.top) + y);
1052 	    }
1053 	}
1054       else
1055 #endif
1056 	{
1057 	  RGBForeColor(&fg_col);
1058 
1059 	  for (pos = 0; pos<length; pos++)
1060 	    {
1061 	      plot_font_3(string[pos],
1062 			  portRect.left+x + xfont_x*pos,
1063 			  portRect.top-y - xfont_y);
1064 	    }
1065 	}
1066 
1067       return;
1068     }
1069 
1070 #ifdef USE_ATS
1071  {
1072    ATSUTextLayout lo;
1073     ATSUTextMeasurement before, after, ascent, descent;
1074 
1075    ATSUCreateTextLayout(&lo);
1076    make_layout(font, string, length, lo);
1077 
1078    RGBForeColor(&fg_col);
1079    ATSUDrawText(lo, 0, length, (portRect.left + x)*65536.0,
1080 		(portRect.top - y)*65536.0);
1081    ATSUDisposeTextLayout(lo);
1082  }
1083 #else
1084   outbuf = convert_text(font, string, length, &outlen);
1085 
1086 # ifdef USE_QUARTZ
1087   if (!enable_quartz)
1088     {
1089 # endif
1090       select_font(font);
1091 
1092       RGBBackColor(&bg_col);
1093       RGBForeColor(&fg_col);
1094       MoveTo(portRect.left+x, portRect.top - y);
1095       DrawText(outbuf, 0, outlen);
1096 # ifdef USE_QUARTZ
1097     }
1098   else
1099     {
1100       CGGlyph* glyph;
1101 
1102       glyph = convert_glyphs(font, string, length, &outlen);
1103 
1104       if (outlen <= 0)
1105 	return;
1106 
1107       /*
1108        * There is always CGContextSetFont, but while I'm able to create
1109        * the structure, I can't make CGContextShowText display *anything*...
1110        */
1111       if (winlastfont != font)
1112 	{
1113 	  CGContextSetFont(carbon_quartz_context, font->data.mac.cgfont);
1114 	  CGContextSetFontSize(carbon_quartz_context, font->data.mac.size);
1115 	  winlastfont = font;
1116 	}
1117 
1118       CGContextSetRGBFillColor(carbon_quartz_context,
1119 			       (float)fg_col.red/65536.0,
1120 			       (float)fg_col.green/65536.0,
1121 			       (float)fg_col.blue/65536.0,
1122 			       1.0);
1123 
1124       CGContextSetTextDrawingMode(carbon_quartz_context, kCGTextFill);
1125       CGContextSetTextPosition(carbon_quartz_context,
1126 			       portRect.left + x,
1127 			       (portRect.bottom-portRect.top)+y);
1128       CGContextShowGlyphsAtPoint(carbon_quartz_context,
1129 				 portRect.left + x,
1130 				 (portRect.bottom-portRect.top)+y,
1131 				 glyph, outlen);
1132     }
1133 # endif
1134 #endif
1135 }
1136 
1137 #endif
1138