1 // March 12, 2005
2 //
3 //      PLplot driver for AquaTerm and Mac OS X.
4 //
5 //      Copyright (C) Per Persson
6 //      Copyright (C) 2005 Hazen Babcock
7 //
8 //      This file is part of PLplot.
9 //
10 //      PLplot is free software; you can redistribute it and/or modify
11 //      it under the terms of the GNU Library General Public License as published
12 //      by the Free Software Foundation; either version 2 of the License, or
13 //      (at your option) any later version.
14 //
15 //      PLplot is distributed in the hope that it will be useful,
16 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 //      GNU Library General Public License for more details.
19 //
20 //      You should have received a copy of the GNU Library General Public License
21 //      along with PLplot; if not, write to the Free Software
22 //      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 //
25 
26 //---------------------------------------------
27 // Header files, defines and local variables
28 // ---------------------------------------------
29 
30 // OS X specific header files
31 
32 #import <Foundation/Foundation.h>
33 #import <AquaTerm/AQTAdapter.h>
34 
35 // PLplot header files
36 
37 #include "plplotP.h"
38 #include "drivers.h"
39 
40 // constants
41 
42 #define SCALE             0.1
43 #define AQT_Default_X     720
44 #define AQT_Default_Y     540
45 #define DPI               72.0
46 
47 #define MAX_STRING_LEN    1000
48 
49 // local variables
50 
51 static NSAutoreleasePool *arpool;     // Objective-C autorelease pool
52 static id adapter;                    // Adapter object
53 
54 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_aqt = "aqt:AquaTerm (Mac OS X):1:aqt:50:aqt\n";
55 
56 static int  currentPlot = 0;
57 static int  maxWindows  = 30;
58 static int  windowXSize = 0;
59 static int  windowYSize = 0;
60 
61 static bool didTests = false;
62 static bool hasShear = false;
63 static bool hasAlpha = false;
64 
65 // font stuff
66 
67 //
68 //      AquaTerm font look-up table
69 //
70 //      The table is initialized with lowest common denominator truetype
71 //      fonts that (I hope) most Macs will have.
72 //
73 
74 #define AQT_N_FontLookup    30
75 static FCI_to_FontName_Table AQT_FontLookup[AQT_N_FontLookup] = {
76     { PL_FCI_MARK | 0x000, (unsigned char *) "Helvetica"             },
77     { PL_FCI_MARK | 0x001, (unsigned char *) "Times-Roman"           },
78     { PL_FCI_MARK | 0x002, (unsigned char *) "Courier"               },
79     { PL_FCI_MARK | 0x003, (unsigned char *) "Times-Roman"           },
80     { PL_FCI_MARK | 0x004, (unsigned char *) "LucidaGrande Regular"  },
81     { PL_FCI_MARK | 0x010, (unsigned char *) "Helvetica-Oblique"     },
82     { PL_FCI_MARK | 0x011, (unsigned char *) "Times-Italic"          },
83     { PL_FCI_MARK | 0x012, (unsigned char *) "Courier-Oblique"       },
84     { PL_FCI_MARK | 0x013, (unsigned char *) "Times-Italic"          },
85     { PL_FCI_MARK | 0x014, (unsigned char *) "LucidaGrande Regular"  },
86     { PL_FCI_MARK | 0x020, (unsigned char *) "Helvetica-Oblique"     },
87     { PL_FCI_MARK | 0x021, (unsigned char *) "Times-Italic"          },
88     { PL_FCI_MARK | 0x022, (unsigned char *) "Courier-Oblique"       },
89     { PL_FCI_MARK | 0x023, (unsigned char *) "Times-Italic"          },
90     { PL_FCI_MARK | 0x024, (unsigned char *) "LucidaGrande Regular"  },
91     { PL_FCI_MARK | 0x100, (unsigned char *) "Helvetica-Bold"        },
92     { PL_FCI_MARK | 0x101, (unsigned char *) "Times-Bold"            },
93     { PL_FCI_MARK | 0x102, (unsigned char *) "Courier-Bold"          },
94     { PL_FCI_MARK | 0x103, (unsigned char *) "Times-Bold"            },
95     { PL_FCI_MARK | 0x104, (unsigned char *) "LucidaGrande Regular"  },
96     { PL_FCI_MARK | 0x110, (unsigned char *) "Helvetica-BoldOblique" },
97     { PL_FCI_MARK | 0x111, (unsigned char *) "Times-BoldItalic"      },
98     { PL_FCI_MARK | 0x112, (unsigned char *) "Courier-BoldOblique"   },
99     { PL_FCI_MARK | 0x113, (unsigned char *) "Times-BoldItalic"      },
100     { PL_FCI_MARK | 0x114, (unsigned char *) "LucidaGrande Regular"  },
101     { PL_FCI_MARK | 0x120, (unsigned char *) "Helvetica-BoldOblique" },
102     { PL_FCI_MARK | 0x121, (unsigned char *) "Times-BoldItalic"      },
103     { PL_FCI_MARK | 0x122, (unsigned char *) "Courier-BoldOblique"   },
104     { PL_FCI_MARK | 0x123, (unsigned char *) "Times-BoldItalic"      },
105     { PL_FCI_MARK | 0x124, (unsigned char *) "LucidaGrande Regular"  }
106 };
107 
108 //
109 //      AquaTerm font environment variables
110 //
111 //      When the driver is initialized it will check to see if
112 //      the user has opted to overide one of the above fonts by
113 //      setting one of the environment variables below.
114 //
115 //      This list must be in the same order with the same number of
116 //      elements as the above list
117 //
118 //      These are the same environment variable names as would be used
119 //      on a linux system, but they have a slightly different meaning.
120 //      Since AquaTerm will find the font for us (if it can) given
121 //      just the font name, you should only set the environment
122 //      variable to the font name. You don't need to provide
123 //      a path. If you installed the font using Font Book, AquaTerm
124 //      should not have any trouble finding it.
125 //
126 //      FIXME: Would it be better to use different environment variable
127 //              names then plfreetype.c? If not, then it probably isn't
128 //              ideal to have two different copies of the same list of
129 //              environment variable names.
130 //
131 
132 const char *aqt_font_env_names[AQT_N_FontLookup] = {
133     "PLPLOT_FREETYPE_SANS_FONT",
134     "PLPLOT_FREETYPE_SERIF_FONT",
135     "PLPLOT_FREETYPE_MONO_FONT",
136     "PLPLOT_FREETYPE_SCRIPT_FONT",
137     "PLPLOT_FREETYPE_SYMBOL_FONT",
138     "PLPLOT_FREETYPE_SANS_ITALIC_FONT",
139     "PLPLOT_FREETYPE_SERIF_ITALIC_FONT",
140     "PLPLOT_FREETYPE_MONO_ITALIC_FONT",
141     "PLPLOT_FREETYPE_SCRIPT_ITALIC_FONT",
142     "PLPLOT_FREETYPE_SYMBOL_ITALIC_FONT",
143     "PLPLOT_FREETYPE_SANS_OBLIQUE_FONT",
144     "PLPLOT_FREETYPE_SERIF_OBLIQUE_FONT",
145     "PLPLOT_FREETYPE_MONO_OBLIQUE_FONT",
146     "PLPLOT_FREETYPE_SCRIPT_OBLIQUE_FONT",
147     "PLPLOT_FREETYPE_SYMBOL_OBLIQUE_FONT",
148     "PLPLOT_FREETYPE_SANS_BOLD_FONT",
149     "PLPLOT_FREETYPE_SERIF_BOLD_FONT",
150     "PLPLOT_FREETYPE_MONO_BOLD_FONT",
151     "PLPLOT_FREETYPE_SCRIPT_BOLD_FONT",
152     "PLPLOT_FREETYPE_SYMBOL_BOLD_FONT",
153     "PLPLOT_FREETYPE_SANS_BOLD_ITALIC_FONT",
154     "PLPLOT_FREETYPE_SERIF_BOLD_ITALIC_FONT",
155     "PLPLOT_FREETYPE_MONO_BOLD_ITALIC_FONT",
156     "PLPLOT_FREETYPE_SCRIPT_BOLD_ITALIC_FONT",
157     "PLPLOT_FREETYPE_SYMBOL_BOLD_ITALIC_FONT",
158     "PLPLOT_FREETYPE_SANS_BOLD_OBLIQUE_FONT",
159     "PLPLOT_FREETYPE_SERIF_BOLD_OBLIQUE_FONT",
160     "PLPLOT_FREETYPE_MONO_BOLD_OBLIQUE_FONT",
161     "PLPLOT_FREETYPE_SCRIPT_BOLD_OBLIQUE_FONT",
162     "PLPLOT_FREETYPE_SYMBOL_BOLD_OBLIQUE_FONT"
163 };
164 
165 // Debugging extras
166 
NOOP_(id x,...)167 static inline void NOOP_( id x, ... )
168 {
169     ;
170 }
171 
172 #ifdef LOGGING
173 #define LOG    NSLog
174 #else
175 #define LOG    NOOP_
176 #endif  // LOGGING
177 
178 //-----------------------------------------------
179 // function declarations
180 // -----------------------------------------------
181 
182 // helper functions
183 
184 static void get_cursor( PLStream *, PLGraphicsIn * );
185 static void proc_str( PLStream *, EscText * );
186 NSMutableAttributedString * create_string( const PLUNICODE *, int, PLFLT );
187 static void set_font_and_size( NSMutableAttributedString *, PLUNICODE, PLFLT, int );
188 static void check_font_environment_variables( void );
189 
190 // PLplot interface functions
191 
192 void plD_dispatch_init_aqt( PLDispatchTable *pdt );
193 void plD_init_aqt( PLStream * );
194 void plD_line_aqt( PLStream *, short, short, short, short );
195 void plD_polyline_aqt( PLStream *, short *, short *, PLINT );
196 void plD_eop_aqt( PLStream * );
197 void plD_bop_aqt( PLStream * );
198 void plD_tidy_aqt( PLStream * );
199 void plD_state_aqt( PLStream *, PLINT );
200 void plD_esc_aqt( PLStream *, PLINT, void * );
201 
202 //--------------------------------------------------------------------------
203 // dispatch_init_init()
204 //
205 // Initialize device dispatch table
206 //--------------------------------------------------------------------------
207 
plD_dispatch_init_aqt(PLDispatchTable * pdt)208 void plD_dispatch_init_aqt( PLDispatchTable *pdt )
209 {
210 #ifndef ENABLE_DYNDRIVERS
211     pdt->pl_MenuStr = "AquaTerm - Mac OS X";
212     pdt->pl_DevName = "aqt";
213 #endif
214     pdt->pl_type     = plDevType_Interactive;
215     pdt->pl_seq      = 1;
216     pdt->pl_init     = (plD_init_fp) plD_init_aqt;
217     pdt->pl_line     = (plD_line_fp) plD_line_aqt;
218     pdt->pl_polyline = (plD_polyline_fp) plD_polyline_aqt;
219     pdt->pl_eop      = (plD_eop_fp) plD_eop_aqt;
220     pdt->pl_bop      = (plD_bop_fp) plD_bop_aqt;
221     pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_aqt;
222     pdt->pl_state    = (plD_state_fp) plD_state_aqt;
223     pdt->pl_esc      = (plD_esc_fp) plD_esc_aqt;
224 }
225 
226 //--------------------------------------------------------------------------
227 // aqt_init()
228 //
229 // Initialize device
230 //--------------------------------------------------------------------------
231 
plD_init_aqt(PLStream * pls)232 void plD_init_aqt( PLStream *pls )
233 {
234     if ( arpool == NULL ) // Make sure we don't leak mem by allocating every time
235     {
236         arpool  = [[NSAutoreleasePool alloc] init];
237         adapter = [[AQTAdapter alloc] init];
238     }
239     [adapter setBackgroundColorRed : 0.5 green : 0.5 blue : 0.5];
240 
241     pls->termin      = 1;               // interactive device
242     pls->dev_flush   = 1;               // Handle our own flushes
243     pls->color       = 1;               // supports color
244     pls->width       = 1;
245     pls->verbose     = 1;
246     pls->bytecnt     = 0;
247     pls->debug       = 1;
248     pls->dev_text    = 1;       // handles text
249     pls->dev_unicode = 1;       // wants text as unicode
250     pls->page        = 0;
251     pls->dev_fill0   = 1;       // supports hardware solid fills
252     pls->dev_fill1   = 1;
253 
254     pls->graphx = GRAPHICS_MODE;
255 
256     if ( !pls->colorset )
257         pls->color = 1;
258 
259     // Set up device parameters
260 
261     plP_setpxl( DPI / 25.4 / SCALE, DPI / 25.4 / SCALE ); // Pixels/mm.
262 
263     // Set the bounds for plotting.  default is AQT_Default_X x AQT_Default_Y unless otherwise specified.
264 
265     if ( pls->xlength <= 0 || pls->ylength <= 0 )
266     {
267         windowXSize = AQT_Default_X;
268         windowYSize = AQT_Default_Y;
269         plP_setphy( (PLINT) 0, (PLINT) ( AQT_Default_X / SCALE ), (PLINT) 0, (PLINT) ( AQT_Default_Y / SCALE ) );
270     }
271     else
272     {
273         windowXSize = pls->xlength;
274         windowYSize = pls->ylength;
275         plP_setphy( (PLINT) 0, (PLINT) ( pls->xlength / SCALE ), (PLINT) 0, (PLINT) ( pls->ylength / SCALE ) );
276     }
277 
278     // check font environment variables & update font table as necessary
279 
280     check_font_environment_variables();
281 
282     // Check to see if the users version of aquaterm supports sheared labels.
283     // If it isn't available 3D plots will look a little strange but things should otherwise be okay.
284 
285     if ( !didTests )
286     {
287         hasShear = [adapter respondsToSelector:@selector( addLabel:atPoint:angle:shearAngle:align: )];
288         hasAlpha = [adapter respondsToSelector:@selector( setColorRed:green:blue:alpha: )];
289         didTests = true;
290     }
291 }
292 
293 //--------------------------------------------------------------------------
294 // aqt_bop()
295 //
296 // Set up for the next page.
297 //--------------------------------------------------------------------------
298 
plD_bop_aqt(PLStream * pls)299 void plD_bop_aqt( PLStream *pls )
300 {
301     currentPlot = currentPlot >= maxWindows ? 0 : currentPlot;
302     [adapter openPlotWithIndex : currentPlot++];
303     [adapter setPlotSize : NSMakeSize( windowXSize, windowYSize )];
304     [adapter setLinewidth : 1.0];
305     if ( hasAlpha )
306     {
307         [adapter setColorRed : (float) ( pls->curcolor.r / 255. )
308          green : (float) ( pls->curcolor.g / 255. )
309          blue : (float) ( pls->curcolor.b / 255. )
310          alpha : (float) ( pls->curcolor.a )];
311     }
312     else
313     {
314         [adapter setColorRed : (float) ( pls->curcolor.r / 255. )
315          green : (float) ( pls->curcolor.g / 255. )
316          blue : (float) ( pls->curcolor.b / 255. )];
317     }
318 
319     pls->page++;
320 }
321 
322 //--------------------------------------------------------------------------
323 // aqt_line()
324 //
325 // Draw a line in the current color from (x1,y1) to (x2,y2).
326 //--------------------------------------------------------------------------
327 
plD_line_aqt(PLStream * pls,short x1a,short y1a,short x2a,short y2a)328 void plD_line_aqt( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
329 {
330     [adapter moveToPoint : NSMakePoint( (float) x1a * SCALE, (float) y1a * SCALE )];
331     [adapter addLineToPoint : NSMakePoint( (float) x2a * SCALE, (float) y2a * SCALE )];
332 }
333 
334 //--------------------------------------------------------------------------
335 // aqt_polyline()
336 //
337 // Draw a polyline in the current color.
338 //--------------------------------------------------------------------------
339 
plD_polyline_aqt(PLStream * pls,short * xa,short * ya,PLINT npts)340 void plD_polyline_aqt( PLStream *pls, short *xa, short *ya, PLINT npts )
341 {
342     int i;
343 
344     for ( i = 0; i < npts - 1; i++ )
345         plD_line_aqt( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] );
346 }
347 
348 //--------------------------------------------------------------------------
349 // aqt_eop()
350 //
351 // End of page
352 //--------------------------------------------------------------------------
353 
plD_eop_aqt(PLStream * pls)354 void plD_eop_aqt( PLStream *pls )
355 {
356     [arpool release]; // prevents a memory leak by freeing everything in
357                       // the auto-release pool when the plot is closed.
358     arpool = [[NSAutoreleasePool alloc] init];
359     [adapter renderPlot];
360 }
361 
362 //--------------------------------------------------------------------------
363 // aqt_tidy()
364 //
365 // Close graphics file or otherwise clean up.
366 //--------------------------------------------------------------------------
367 
plD_tidy_aqt(PLStream * pls)368 void plD_tidy_aqt( PLStream *pls )
369 {
370     [adapter closePlot];
371 }
372 
373 //--------------------------------------------------------------------------
374 // plD_state_aqt()
375 //
376 // Handle change in PLStream state (color, pen width, fill attribute, etc).
377 //--------------------------------------------------------------------------
378 
plD_state_aqt(PLStream * pls,PLINT op)379 void plD_state_aqt( PLStream *pls, PLINT op )
380 {
381     int   i;
382     float r, g, b;
383 
384     switch ( op )
385     {
386     case PLSTATE_WIDTH:
387         [adapter setLinewidth : (float) pls->width];
388         break;
389 
390     case PLSTATE_COLOR0:        // this seems to work, but that isn't to say that it is done right...
391         if ( hasAlpha )
392         {
393             [adapter setBackgroundColorRed : (float) ( plsc->cmap0[0].r / 255.0 )
394              green : (float) ( plsc->cmap0[0].g / 255.0 )
395              blue : (float) ( plsc->cmap0[0].b / 255.0 )
396              alpha : (float) ( plsc->cmap0[0].a )];
397         }
398         else
399         {
400             [adapter setBackgroundColorRed : (float) ( plsc->cmap0[0].r / 255.0 )
401              green : (float) ( plsc->cmap0[0].g / 255.0 )
402              blue : (float) ( plsc->cmap0[0].b / 255.0 )];
403         }
404     case PLSTATE_COLOR1:
405     case PLSTATE_FILL:
406         if ( hasAlpha )
407         {
408             [adapter setColorRed : (float) ( pls->curcolor.r / 255. )
409              green : (float) ( pls->curcolor.g / 255. )
410              blue : (float) ( pls->curcolor.b / 255. )
411              alpha : (float) ( pls->curcolor.a )];
412         }
413         else
414         {
415             [adapter setColorRed : (float) ( pls->curcolor.r / 255. )
416              green : (float) ( pls->curcolor.g / 255. )
417              blue : (float) ( pls->curcolor.b / 255. )];
418         }
419         break;
420 
421     case PLSTATE_CMAP0:
422         break;
423 
424     case PLSTATE_CMAP1:
425         break;
426     }
427 }
428 
429 //--------------------------------------------------------------------------
430 // aqt_esc()
431 //
432 // Escape function.
433 //
434 // Functions:
435 //
436 // PLESC_EH        Handle pending events
437 // PLESC_EXPOSE    Force an expose
438 // PLESC_FILL      Fill polygon
439 // PLESC_FLUSH     Flush X event buffer
440 // PLESC_GETC      Get coordinates upon mouse click
441 // PLESC_REDRAW    Force a redraw
442 // PLESC_RESIZE    Force a resize
443 //--------------------------------------------------------------------------
444 
plD_esc_aqt(PLStream * pls,PLINT op,void * ptr)445 void plD_esc_aqt( PLStream *pls, PLINT op, void *ptr )
446 {
447     int i;
448     switch ( op )
449     {
450     case PLESC_EXPOSE:             // handle window expose
451         break;
452     case PLESC_RESIZE:             // handle window resize
453         break;
454     case PLESC_REDRAW:             // handle window redraw
455         break;
456     case PLESC_TEXT:               // switch to text screen
457         break;
458     case PLESC_GRAPH:              // switch to graphics screen
459         break;
460     case PLESC_FILL:               // fill polygon
461         [adapter moveToVertexPoint : NSMakePoint( pls->dev_x[0] * SCALE, pls->dev_y[0] * SCALE )];
462         for ( i = 1; i < pls->dev_npts; i++ )
463         {
464             [adapter addEdgeToVertexPoint : NSMakePoint( pls->dev_x[i] * SCALE, pls->dev_y[i] * SCALE )];
465         }
466         ;
467         break;
468     case PLESC_DI:                 // handle DI command
469         break;
470     case PLESC_FLUSH:              // flush output
471         [adapter renderPlot];
472         break;
473     case PLESC_EH:                 // handle Window events
474         break;
475     case PLESC_GETC:               // get cursor position
476         [adapter renderPlot];      // needed to give the user something to click on
477         get_cursor( pls, (PLGraphicsIn *) ptr );
478         break;
479     case PLESC_SWIN:               // set window parameters
480         break;
481     case PLESC_HAS_TEXT:
482         proc_str( pls, (EscText *) ptr );
483         break;
484     }
485 }
486 
487 //--------------------------------------------------------------------------
488 // get_cursor()
489 //
490 // returns the location of the next mouse click
491 //--------------------------------------------------------------------------
492 
get_cursor(PLStream * pls,PLGraphicsIn * gin)493 void get_cursor( PLStream *pls, PLGraphicsIn *gin )
494 {
495     int      scanned, x, y, button;
496     NSString *temp;
497 
498     plGinInit( gin );
499 
500     temp    = [adapter waitNextEvent];
501     scanned = sscanf([temp cString], "1:{%d, %d}:%d", &x, &y, &button );
502 
503     if ( scanned == 3 )         // check that we did actually get a reasonable event string
504     {
505         gin->button = button;
506         gin->pX     = x;
507         gin->pY     = y;
508         gin->dX     = (PLFLT) x / ( (PLFLT) ( pls->xlength ) );
509         gin->dY     = (PLFLT) y / ( (PLFLT) ( pls->ylength ) );
510     }
511     else                // just return zeroes if we did not
512     {
513         printf( "AquaTerm did not return a valid mouse location!\n" );
514         gin->button = 0;
515         gin->pX     = 0;
516         gin->pY     = 0;
517         gin->dX     = 0.0;
518         gin->dY     = 0.0;
519     }
520 }
521 
522 //--------------------------------------------------------------------------
523 // proc_str()
524 //
525 // Processes strings for display. The actual parsing of the unicode
526 // string is handled by the sub-routine create_string.
527 //--------------------------------------------------------------------------
528 
proc_str(PLStream * pls,EscText * args)529 void proc_str( PLStream *pls, EscText *args )
530 {
531     PLFLT a1, ft_ht, angle, shear, stride;
532     PLINT clxmin, clxmax, clymin, clymax;
533     int   i, jst, ref;
534     NSMutableAttributedString *str;
535 
536     // check that we got unicode, warning message and return if not
537 
538     if ( args->unicode_array_len == 0 )
539     {
540         printf( "Non unicode string passed to AquaTerm driver, ignoring\n" );
541         return;
542     }
543 
544     // check that unicode string isn't longer then the max we allow
545 
546     if ( args->unicode_array_len >= MAX_STRING_LEN )
547     {
548         printf( "Sorry, the AquaTerm driver only handles strings of length < %d\n", MAX_STRING_LEN );
549         return;
550     }
551 
552     // set the font height - the 1.2 factor was trial and error
553 
554     ft_ht = 1.2 * pls->chrht * DPI / 25.4; // ft_ht in points. ht is in mm
555 
556     // given transform, calculate rotation angle & shear angle
557     plRotationShear( args->xform, &angle, &shear, &stride );
558     angle *= 180.0 / PI;
559     shear *= -180.0 / PI;
560 
561     // text justification, AquaTerm only supports 3 options, so we round appropriately
562 
563     if ( args->just < 0.33 )
564         jst = AQTAlignLeft;                         // left
565     else if ( args->just > 0.66 )
566         jst = AQTAlignRight;                        // right
567     else
568         jst = AQTAlignCenter;                       // center
569 
570     // set the baseline of the string
571     // Middle and Bottom are set to Middle since this seems to be what PLplot expects
572     // as judged by where it renders the symbols in example 1.
573 
574     if ( args->base == 2 )      // Top
575         ref = AQTAlignTop;
576     else if ( args->base == 1 ) // Bottom
577         ref = AQTAlignMiddle;
578     else
579         ref = AQTAlignMiddle; // Middle
580 
581     // create an appropriately formatted, etc... unicode string
582 
583     str = create_string( args->unicode_array, args->unicode_array_len, ft_ht );
584 
585     // display the string
586 
587     if ( hasAlpha )
588     {
589         [adapter setColorRed : (float) ( pls->curcolor.r / 255. )
590          green : (float) ( pls->curcolor.g / 255. )
591          blue : (float) ( pls->curcolor.b / 255. )
592          alpha : (float) ( pls->curcolor.a )];
593     }
594     else
595     {
596         [adapter setColorRed : (float) ( pls->curcolor.r / 255. )
597          green : (float) ( pls->curcolor.g / 255. )
598          blue : (float) ( pls->curcolor.b / 255. )];
599     }
600 
601     if ( hasShear )
602     {
603         [adapter addLabel : str
604          atPoint : NSMakePoint( (float) args->x * SCALE, (float) args->y * SCALE )
605          angle : angle
606          shearAngle : shear
607          align : ( jst | ref )];
608     }
609     else
610     {
611         [adapter addLabel : str
612          atPoint : NSMakePoint( (float) args->x * SCALE, (float) args->y * SCALE )
613          angle : angle
614          align : ( jst | ref )];
615     }
616 
617     [str release];
618 }
619 
620 //--------------------------------------------------------------------------
621 // create_string()
622 //
623 // create a NSMutableAttributedString from the plplot ucs4 string
624 //
625 // assumptions :
626 // 1. font changes are unicode >= PL_FCI_MARK
627 // 2. we'll never have to deal with a string longer then MAX_STRING_LEN characters
628 // 3. <esc><esc> means we desired <esc> as a character & not actually as <esc>
629 // 4. there are no two character <esc> sequences... i.e. <esc>fn is now covered by fci
630 //
631 //--------------------------------------------------------------------------
632 
create_string(const PLUNICODE * ucs4,int ucs4_len,PLFLT font_height)633 NSMutableAttributedString  * create_string( const PLUNICODE *ucs4, int ucs4_len, PLFLT font_height )
634 {
635     PLUNICODE fci;
636     char      plplot_esc;
637     int       i;
638     int       cur_loc;
639     int       utf8_len;
640     int       updown;
641     char      dummy[MAX_STRING_LEN + 1];
642     char                      *font;
643     char      utf8[5];
644     NSMutableAttributedString *str;
645 
646     updown = 0;
647 
648     // initialize the attributed string
649 
650     for ( i = 0; i < MAX_STRING_LEN; i++ )
651         dummy[i] = 'i';
652     dummy[MAX_STRING_LEN] = '\0';
653     str = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithCString:dummy]];
654 
655     // get plplot escape character & current font
656 
657     plgesc( &plplot_esc );
658     plgfci( &fci );
659 
660     // set the font for the string based on the current font & size
661 
662     set_font_and_size( str, fci, font_height, 0 );
663 
664     // parse plplot ucs4 string
665 
666     cur_loc = 0;
667     i       = 0;
668     while ( i < ucs4_len )
669     {
670         if ( ucs4[i] < PL_FCI_MARK )                                    // not a font change
671         {
672             if ( ucs4[i] != (PLUNICODE) plplot_esc )                    // a character to display
673             {
674                 ucs4_to_utf8( ucs4[i], utf8 );
675                 [str replaceCharactersInRange : NSMakeRange( cur_loc, 1 )
676                  withString :[NSString stringWithUTF8String : utf8]];
677                 i++;
678                 cur_loc++;
679                 continue;
680             }
681             i++;
682             if ( ucs4[i] == (PLUNICODE) plplot_esc )
683             {
684                 ucs4_to_utf8( ucs4[i], utf8 );
685                 [str replaceCharactersInRange : NSMakeRange( cur_loc, 1 )
686                  withString :[NSString stringWithUTF8String : utf8]];
687                 i++;
688                 cur_loc++;
689                 continue;
690             }
691             else
692             {
693                 if ( ucs4[i] == (PLUNICODE) 'f' )       // font change
694                 {
695                     i++;
696                     printf( "hmm, unicode string apparently not following fci convention...\n" );
697                 }
698                 if ( ucs4[i] == (PLUNICODE) 'd' )       // Subscript
699                 {
700                     updown--;
701                     [str addAttribute : @ "NSSuperScript"
702                      value :[NSNumber numberWithInt : updown]
703                      range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )];
704                 }
705                 if ( ucs4[i] == (PLUNICODE) 'u' )       // Superscript
706                 {
707                     updown++;
708                     [str addAttribute : @ "NSSuperScript"
709                      value :[NSNumber numberWithInt : updown]
710                      range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )];
711                 }
712                 i++;
713             }
714         }
715         else    // a font change
716         {
717             set_font_and_size( str, ucs4[i], font_height, cur_loc );
718             i++;
719         }
720     }
721 
722     // trim string to appropriate final length
723 
724     [str deleteCharactersInRange : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )];
725 
726     return str;
727 }
728 
729 //--------------------------------------------------------------------------
730 // set_font_and_size
731 //
732 // set the font & size of a attributable string object
733 //--------------------------------------------------------------------------
734 
set_font_and_size(NSMutableAttributedString * str,PLUNICODE fci,PLFLT font_height,int cur_loc)735 void set_font_and_size( NSMutableAttributedString * str, PLUNICODE fci, PLFLT font_height, int cur_loc )
736 {
737     char *font;
738 
739     font = plP_FCI2FontName( fci, AQT_FontLookup, AQT_N_FontLookup );
740 
741     // check whether that font exists & if not, use standard font instead
742 
743     if ( font == NULL )
744     {
745         printf( "AquaTerm : Warning, could not find font given by fci = 0x%x\n", fci );
746         font = "Helvetica";
747     }
748     /* font = "FreeSerif";	*//* force the font for debugging purposes */
749     // printf("Font at %d is : %s\n", cur_loc, font);
750 
751     [str addAttribute : @ "AQTFontname"
752      value :[NSString stringWithCString : font]
753      range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )];
754     [str addAttribute : @ "AQTFontsize"
755      value :[NSNumber numberWithFloat : font_height]
756      range : NSMakeRange( cur_loc, ( MAX_STRING_LEN - cur_loc ) )];
757 }
758 
759 //--------------------------------------------------------------------------
760 // check_font_environment_variables
761 //
762 // Checks to see if any font environment variables are defined.
763 // If a font environment variable is defined, then the appropriate
764 // element of the default font table is replaced with the font name
765 // string specified by the environment variable.
766 //--------------------------------------------------------------------------
767 
768 
check_font_environment_variables(void)769 void check_font_environment_variables( void )
770 {
771     int  i;
772     char *new_font;
773     char *begin;
774     char *end;
775 
776     for ( i = 0; i < AQT_N_FontLookup; i++ )
777     {
778         if ( ( new_font = getenv( aqt_font_env_names[i] ) ) != NULL )
779         {
780             // If the user is just blindly following the suggestions in
781             // the plplot examples then we might get a font name with
782             // a path and extension. We need to remove that since it
783             // isn't relevant and will only cause trouble. We warn them
784             // AquaTerm was not expecting a path or extension.
785 
786             begin = strrchr( new_font, '/' );
787             end   = strrchr( new_font, '.' );
788 
789             if ( end != NULL )
790             {
791                 printf( "Aquaterm : Warning, removing extension from font name : %s\n", new_font );
792                 *end = '\0';
793             }
794             if ( begin != NULL )
795             {
796                 printf( "AquaTerm : Warning, removing path from font name : %s\n", new_font );
797                 new_font = begin + 1;
798             }
799 
800             // printf("new font : %s\n", new_font);
801 
802             AQT_FontLookup[i].pfont = (unsigned char *) new_font;
803         }
804     }
805 }
806