1 #include "RDeviceImpl.h"
2 #include <Rinternals.h>
6 #include <R_ext/GraphicsDevice.h>
7 #include <R_ext/GraphicsEngine.h>
10 double ps
,scale
,width
,height
,tscale
;
11 int dirty
,bg
,gstate
,antialias
,redraw
;
13 NewDevDesc
*dev
; //Circular reference.
15 CGContextRef (*getCGContext
)(QuartzDesc_t dev
,void*userInfo
);
16 int (*locatePoint
)(QuartzDesc_t dev
,void*userInfo
,double*x
,double*y
);
17 void (*close
)(QuartzDesc_t dev
,void*userInfo
);
18 void (*newPage
)(QuartzDesc_t dev
,void*userInfo
);
19 void (*activate
)(QuartzDesc_t dev
,void*userInfo
,int devNum
);
20 void (*deactivate
)(QuartzDesc_t dev
,void*userInfo
,int devNum
);
24 #pragma mark QuartzDesc manipulation
27 void RDevice_Update(QuartzDesc_t desc
);
29 double RDevice_ScaledWidth(QuartzDesc
*qd
) { return qd
->scale
*qd
->width
*72.0; }
30 double RDevice_ScaledHeight(QuartzDesc
*qd
) { return qd
->scale
*qd
->height
*72.0; }
33 double RDevice_GetWidth(QuartzDesc_t desc
) { return ((QuartzDesc
*)desc
)->width
; }
34 void RDevice_SetWidth(QuartzDesc_t desc
,double width
) { ((QuartzDesc
*)desc
)->width
= width
;((QuartzDesc
*)desc
)->dev
->right
= RDevice_ScaledWidth(desc
); }
36 double RDevice_GetHeight(QuartzDesc_t desc
) { return ((QuartzDesc
*)desc
)->height
; }
37 void RDevice_SetHeight(QuartzDesc_t desc
,double height
) { ((QuartzDesc
*)desc
)->height
= height
;((QuartzDesc
*)desc
)->dev
->bottom
= RDevice_ScaledHeight(desc
); }
39 double RDevice_GetScale(QuartzDesc_t desc
) { return ((QuartzDesc
*)desc
)->scale
; }
40 void RDevice_SetScale(QuartzDesc_t desc
,double scale
) { ((QuartzDesc
*)desc
)->scale
= scale
;RDevice_Update(desc
); }
42 double RDevice_GetTextScale(QuartzDesc_t desc
) { return ((QuartzDesc
*)desc
)->tscale
; }
43 void RDevice_SetTextScale(QuartzDesc_t desc
,double scale
) { ((QuartzDesc
*)desc
)->tscale
= scale
;RDevice_Update(desc
); }
46 int RDevice_GetDirty(QuartzDesc_t desc
) { return ((QuartzDesc
*)desc
)->dirty
; }
47 void RDevice_SetDirty(QuartzDesc_t desc
,int dirty
) { ((QuartzDesc
*)desc
)->dirty
; }
49 int RDevice_GetGDepth(QuartzDesc_t desc
) { return ((QuartzDesc
*)desc
)->gstate
; }
50 void RDevice_ResetGDepth(QuartzDesc_t desc
) { ((QuartzDesc
*)desc
)->gstate
= 0; }
52 int RDevice_GetAntialias(QuartzDesc_t desc
) { return ((QuartzDesc
*)desc
)->antialias
; }
55 void RDevice_Update(QuartzDesc_t desc
) {
56 QuartzDesc
*qd
= (QuartzDesc
*)desc
;
57 NewDevDesc
*dev
= qd
->dev
;
59 dev
->cra
[0] = 0.9*qd
->scale
*qd
->ps
*qd
->tscale
;
60 dev
->cra
[1] = 1.2*qd
->scale
*qd
->ps
*qd
->tscale
;
61 dev
->ipr
[0] = dev
->ipr
[1] = 1.0/(72.0*qd
->scale
);
64 void RDevice_ReplayDisplayList(QuartzDesc_t desc
) {
65 QuartzDesc
*qd
= (QuartzDesc
*)desc
;
67 if(qd
->dev
->displayList
!= R_NilValue
)
68 GEplayDisplayList((GEDevDesc
*)GetDevice(devNumber((DevDesc
*)qd
->dev
)));
72 void* RDevice_GetSnapshot(QuartzDesc_t desc
) {
73 QuartzDesc
*qd
= (QuartzDesc
*)desc
;
74 return qd
->dev
->savedSnapshot
;
77 void RDevice_RestoreSnapshot(QuartzDesc_t desc
,void* snap
) {
78 QuartzDesc
*qd
= (QuartzDesc
*)desc
;
81 GEplaySnapshot((SEXP
)snap
,(GEDevDesc
*)GetDevice(devNumber((DevDesc
*)qd
->dev
)));
86 #pragma mark Function Prototypes
88 static Rboolean
RQuartz_Open(NewDevDesc
*,QuartzDesc
*,char*,double,double,int);
89 static void RQuartz_Close(NewDevDesc
*);
90 static void RQuartz_Activate(NewDevDesc
*);
91 static void RQuartz_Deactivate(NewDevDesc
*);
92 static void RQuartz_Size(double*,double*,double*,double*,NewDevDesc
*);
93 static void RQuartz_NewPage(R_GE_gcontext
*,NewDevDesc
*);
94 static void RQuartz_Clip(double,double,double,double,NewDevDesc
*);
95 static double RQuartz_StrWidth(char*,R_GE_gcontext
*,NewDevDesc
*);
96 static void RQuartz_Text(double,double,char*,double,double,R_GE_gcontext
*,NewDevDesc
*);
97 static void RQuartz_Rect(double,double,double,double,R_GE_gcontext
*,NewDevDesc
*);
98 static void RQuartz_Circle(double,double,double,R_GE_gcontext
*,NewDevDesc
*);
99 static void RQuartz_Line(double,double,double,double,R_GE_gcontext
*,NewDevDesc
*);
100 static void RQuartz_Polyline(int,double*,double*,R_GE_gcontext
*,NewDevDesc
*);
101 static void RQuartz_Polygon(int,double*,double*,R_GE_gcontext
*,NewDevDesc
*);
102 static Rboolean
RQuartz_Locator(double*,double*,NewDevDesc
*);
103 static void RQuartz_Mode(int mode
,NewDevDesc
*);
104 static void RQuartz_Hold(NewDevDesc
*);
105 static void RQuartz_MetricInfo(int,R_GE_gcontext
*,double*,double*,double*,NewDevDesc
*);
108 void* RDevice_Create(
109 void *_dev
,double scale
,double ps
,double width
,double height
,int bg
,int aa
,
110 CGContextRef (*getCGContext
)(QuartzDesc_t dev
,void*userInfo
), //Get the context for this device
111 int (*locatePoint
)(QuartzDesc_t dev
,void*userInfo
,double*x
,double*y
),
112 void (*close
)(QuartzDesc_t dev
,void*userInfo
),
113 void (*newPage
)(QuartzDesc_t dev
,void*userInfo
),
114 void (*activate
)(QuartzDesc_t dev
,void*userInfo
,int devNum
),
115 void (*deactivate
)(QuartzDesc_t dev
,void*userInfo
,int devNum
),
117 NewDevDesc
*dev
= (NewDevDesc
*)_dev
;
118 dev
->displayList
= R_NilValue
;
120 dev
->startfill
= R_RGB(255,255,255);
121 dev
->startcol
= R_RGB(0,0,0);
124 dev
->startlty
= LTY_SOLID
;
127 //Set up some happy pointers
128 dev
->newDevStruct
= 1;
129 dev
->open
= RQuartz_Open
;
130 dev
->close
= RQuartz_Close
;
131 dev
->activate
= RQuartz_Activate
;
132 dev
->deactivate
= RQuartz_Deactivate
;
133 dev
->size
= RQuartz_Size
;
134 dev
->newPage
= RQuartz_NewPage
;
135 dev
->clip
= RQuartz_Clip
;
136 dev
->strWidth
= RQuartz_StrWidth
;
137 dev
->text
= RQuartz_Text
;
138 dev
->rect
= RQuartz_Rect
;
139 dev
->circle
= RQuartz_Circle
;
140 dev
->line
= RQuartz_Line
;
141 dev
->polyline
= RQuartz_Polyline
;
142 dev
->polygon
= RQuartz_Polygon
;
143 dev
->locator
= RQuartz_Locator
;
144 dev
->mode
= RQuartz_Mode
;
145 dev
->hold
= RQuartz_Hold
;
146 dev
->metricInfo
= RQuartz_MetricInfo
;
152 //Magic numbers from on high.
153 dev
->xCharOffset
= 0.4900;
154 dev
->yCharOffset
= 0.3333;
155 dev
->yLineBias
= 0.20; //This is .2 for PS/PDF devices...
157 dev
->canResizePlot
= TRUE
;
158 dev
->canChangeFont
= TRUE
;
159 dev
->canRotateText
= TRUE
;
160 dev
->canResizeText
= TRUE
;
163 dev
->canChangeGamma
= TRUE
;
164 dev
->displayListOn
= TRUE
;
166 QuartzDesc
*qd
= calloc(1,sizeof(QuartzDesc
));
169 qd
->userInfo
= userInfo
;
170 qd
->getCGContext
= getCGContext
;
171 qd
->locatePoint
= locatePoint
;
173 qd
->newPage
= newPage
;
174 qd
->activate
= activate
;
175 qd
->deactivate
= deactivate
;
183 dev
->deviceSpecific
= qd
;
189 dev
->right
= RDevice_ScaledWidth(qd
)-0.0001;
190 dev
->bottom
= RDevice_ScaledHeight(qd
)-0.0001;
191 qd
->clipRect
= CGRectMake(0,0,dev
->right
,dev
->bottom
);
195 return (QuartzDesc_t
)qd
;
198 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
199 extern CGFontRef
CGFontCreateWithName(CFStringRef
);
200 #define CGFontCreateWithFontName CGFontCreateWithName
201 #define CGFontGetGlyphBBoxes CGFontGetGlyphBoundingBoxes
202 #define CGFontGetGlyphsForUnichars CGFontGetGlyphsForUnicodes
204 //Always needs this one...
205 extern CGFontRef
CGContextGetFont(CGContextRef
);
208 #define DEVDESC NewDevDesc *dd
209 #define CTXDESC R_GE_gcontext*gc,NewDevDesc*dd
211 #define DEVSPEC QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGContextRef ctx = xd->getCGContext(xd,xd->userInfo)
212 #define DRAWSPEC QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;CGContextRef ctx = xd->getCGContext(xd,xd->userInfo);xd->dirty = 1;if(NULL==ctx) { printf("Invalid Context\n");return; }
213 #define XD QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific
214 #pragma mark Device Implementation
216 CFStringRef
RQuartz_FindFont(int fontface
,char *fontfamily
) {
217 SEXP ns
,env
,db
,names
;
219 CFStringRef fontName
= CFSTR("");
220 PROTECT(ns
= R_FindNamespace(ScalarString(mkChar("grDevices"))));
221 PROTECT_WITH_INDEX(env
= findVar(install(".Quartzenv"),ns
),&index
);
222 if(TYPEOF(env
) == PROMSXP
)
223 REPROTECT(env
= eval(env
,ns
),index
);
224 PROTECT(db
= findVar(install(".Quartz.Fonts"),env
));
225 PROTECT(names
= getAttrib(db
,R_NamesSymbol
));
226 if(strlen(fontfamily
)>0) {
228 for(i
=0;i
<length(names
);i
++)
229 if(0 == strcmp(fontfamily
,CHAR(STRING_ELT(names
,i
)))) break;
231 fontName
= CFStringCreateWithCString(kCFAllocatorDefault
,CHAR(STRING_ELT(VECTOR_ELT(db
,i
),fontface
)), kCFStringEncodingUTF8
);
237 CGFontRef
RQuartz_Font(CTXDESC
) {
238 int fontface
= gc
->fontface
;
239 CFMutableStringRef fontName
= CFStringCreateMutable(kCFAllocatorDefault
,0);
240 if((gc
->fontface
== 5) || (strcmp(gc
->fontfamily
,"symbol") == 0))
241 CFStringAppend(fontName
,CFSTR("Symbol"));
243 CFStringRef font
= RQuartz_FindFont(gc
->fontface
,gc
->fontfamily
);
244 if(CFStringGetLength(font
)>0) {
245 fontface
= 1; //This is handled by the lookup process
246 CFStringAppend(fontName
,font
);
250 if(CFStringGetLength(fontName
) == 0)
251 CFStringAppend(fontName
,CFSTR("Arial"));
252 if(fontface
==2 || fontface
== 4) {
253 CFStringAppend(fontName
,CFSTR(" Bold"));
256 CFStringAppend(fontName
,CFSTR(" Italic"));
258 CGFontRef font
= CGFontCreateWithFontName(fontName
);
261 ATSFontRef tmp
= ATSFontFindFromName(fontName
,kATSOptionFlagsDefault
);
262 font
= CGFontCreateWithPlatformFont(&tmp
);
271 #define RQUARTZ_FILL (1)
272 #define RQUARTZ_STROKE (1<<1)
273 #define RQUARTZ_LINE (1<<2)
274 #define RQUARTZ_FONT (1<<3)
276 void RQuartz_Set(CGContextRef ctx
,R_GE_gcontext
*gc
,int flags
) {
277 if(flags
& RQUARTZ_FILL
) {
279 CGContextSetRGBFillColor(ctx
,R_RED(fill
)/256.0,R_GREEN(fill
)/256.0,R_BLUE(fill
)/256.0,R_ALPHA(fill
)/256.0);
281 if(flags
& RQUARTZ_STROKE
) {
282 int stroke
= gc
->col
;
283 CGContextSetRGBStrokeColor(ctx
,R_RED(stroke
)/256.0,R_GREEN(stroke
)/256.0,R_BLUE(stroke
)/256.0,R_ALPHA(stroke
)/256.0);
285 if(flags
& RQUARTZ_LINE
) {
289 CGContextSetLineWidth(ctx
,gc
->lwd
);
291 float lwd
= gc
->lwd
*0.75;
292 for(i
=0;i
<8 && lty
;i
++) {
293 dashlist
[ndash
++] = (lwd
>= 1 ? lwd
: 1)*(lty
&15);
296 CGContextSetLineDash(ctx
,0,dashlist
,ndash
);
299 case GE_ROUND_CAP
:cap
= kCGLineCapRound
;break;
300 case GE_BUTT_CAP
:cap
= kCGLineCapButt
;break;
301 case GE_SQUARE_CAP
:cap
= kCGLineCapSquare
;break;
303 CGContextSetLineCap(ctx
,cap
);
306 case GE_ROUND_JOIN
:join
= kCGLineJoinRound
;break;
307 case GE_MITRE_JOIN
:join
= kCGLineJoinMiter
;break;
308 case GE_BEVEL_JOIN
:join
= kCGLineJoinBevel
;break;
310 CGContextSetLineJoin(ctx
,join
);
311 CGContextSetMiterLimit(ctx
,gc
->lmitre
);
313 if(flags
& RQUARTZ_FONT
) {
314 CGFontRef font
= RQuartz_Font(gc
,NULL
);
315 CGContextSetFont(ctx
,font
);
316 CGContextSetFontSize(ctx
,gc
->cex
*gc
->ps
);
320 #define SET(X) RQuartz_Set(ctx,gc,(X))
322 static Rboolean
RQuartz_Open(DEVDESC
,QuartzDesc
*xd
,char *display
,double width
,double height
,int bg
) {
323 //We don't do anything here.
327 static void RQuartz_Close(DEVDESC
) {
329 if(NULL
!= xd
->close
) xd
->close(xd
,xd
->userInfo
);
332 static void RQuartz_Activate(DEVDESC
) {
334 if(NULL
!= xd
->activate
) xd
->activate(xd
,xd
->userInfo
,devNumber((DevDesc
*)xd
->dev
));
337 static void RQuartz_Deactivate(DEVDESC
) {
339 if(NULL
!= xd
->deactivate
) xd
->deactivate(xd
,xd
->userInfo
,devNumber((DevDesc
*)xd
->dev
));
342 static void RQuartz_Size(double*left
,double*right
,double*bottom
,double*top
,DEVDESC
) {
345 *right
= RDevice_ScaledWidth(xd
);
346 *bottom
= RDevice_ScaledHeight(xd
);
349 static void RQuartz_NewPage(CTXDESC
) {
351 //Should have new page event
352 if(0 == xd
->redraw
&& NULL
!= xd
->newPage
) xd
->newPage(xd
,xd
->userInfo
);
354 CGRect bounds
= CGRectMake(0,0,RDevice_ScaledWidth(xd
),RDevice_ScaledHeight(xd
));
355 if(R_ALPHA(xd
->bg
) == 255 && R_ALPHA(gc
->fill
) == 255)
356 CGContextClearRect(ctx
,bounds
);
357 CGContextFillRect(ctx
,bounds
);
360 static void RQuartz_Clip(double x0
,double x1
,double y0
,double y1
,DEVDESC
) {
364 CGContextRestoreGState(ctx
);
366 CGContextSaveGState(ctx
);
368 if(x1
> x0
) { double t
= x1
;x1
= x0
;x0
= t
; }
369 if(y1
> y0
) { double t
= y1
;y1
= y0
;y0
= t
; }
370 xd
->clipRect
= CGRectMake(x0
,y0
,x1
-x0
,y1
-y0
);
371 CGContextClipToRect(ctx
,xd
->clipRect
);
374 CFStringRef
prepareText(CTXDESC
,char *text
,UniChar
**buffer
,int *free
) {
376 if(gc
->fontface
== 5 || strcmp(gc
->fontfamily
,"symbol") == 0)
377 str
= CFStringCreateWithCString(NULL
,text
,kCFStringEncodingMacSymbol
);
379 str
= CFStringCreateWithCString(NULL
,text
,kCFStringEncodingUTF8
);
380 //Try fallback string encodings if UTF8 doesn't work.
382 CFStringCreateWithCString(NULL
,text
,kCFStringEncodingISOLatin1
);
384 *buffer
= (UniChar
*)CFStringGetCharactersPtr(str
);
385 if (*buffer
== NULL
) {
386 CFIndex length
= CFStringGetLength(str
);
387 *buffer
= malloc(length
* sizeof(UniChar
));
388 CFStringGetCharacters(str
, CFRangeMake(0, length
), *buffer
);
394 static double RQuartz_StrWidth(char *text
,CTXDESC
) {
396 if(ctx
==NULL
) return 0.0;
398 CGFontRef font
= CGContextGetFont(ctx
);
399 float aScale
= (gc
->cex
*gc
->ps
*xd
->tscale
)/CGFontGetUnitsPerEm(font
);
404 CFStringRef str
= prepareText(gc
,dd
,text
,&buffer
,&Free
);
405 len
= CFStringGetLength(str
);
406 glyphs
= malloc(sizeof(CGGlyph
)*len
);
407 advances
= malloc(sizeof(int)*len
);
408 CGFontGetGlyphsForUnichars(font
,buffer
,glyphs
,len
);
409 CGFontGetGlyphAdvances(font
,glyphs
,len
,advances
);
410 float width
= 0.0;// aScale*CGFontGetLeading(CGContextGetFont(ctx));
411 for(i
=0;i
<len
;i
++) width
+= aScale
*advances
[i
];
414 if(Free
) free(buffer
);
419 static void RQuartz_Text(double x
,double y
,char *text
,double rot
,double hadj
,CTXDESC
) {
421 //A stupid hack because R isn't consistent.
424 SET(RQUARTZ_FILL
|RQUARTZ_STROKE
|RQUARTZ_FONT
);
426 CGFontRef font
= CGContextGetFont(ctx
);
427 float aScale
= (gc
->cex
*gc
->ps
*xd
->tscale
)/(CGFontGetUnitsPerEm(font
));
433 CFStringRef str
= prepareText(gc
,dd
,text
,&buffer
,&Free
);
434 len
= CFStringGetLength(str
);
435 glyphs
= malloc(sizeof(CGGlyph
)*len
);
436 CGFontGetGlyphsForUnichars(font
,buffer
,glyphs
,len
);
437 int *advances
= malloc(sizeof(int)*len
);
438 CGSize
*g_adv
= malloc(sizeof(CGSize
)*len
);
440 CGFontGetGlyphAdvances(font
,glyphs
,len
,advances
);
441 for(i
=0;i
<len
;i
++) { width
+= advances
[i
]*aScale
;g_adv
[i
] = CGSizeMake(aScale
*advances
[i
]*cos(-0.0174532925*rot
),aScale
*advances
[i
]*sin(-0.0174532925*rot
)); }
443 CGContextSetTextMatrix(ctx
,CGAffineTransformConcat(CGAffineTransformMakeScale(1.0,-1.0),CGAffineTransformMakeRotation(-0.0174532925*rot
)));
444 double ax
= (width
*hadj
)*cos(-0.0174532925*rot
);
445 double ay
= (width
*hadj
)*sin(-0.0174532925*rot
);
446 // double h = CGFontGetXHeight(CGContextGetFont(ctx))*aScale;
447 CGContextSetTextPosition(ctx
,x
-ax
,y
-ay
);
448 // Rprintf("%s,%.2f %.2f (%.2f,%.2f) (%d,%f)\n",text,hadj,width,ax,ay,CGFontGetUnitsPerEm(CGContextGetFont(ctx)),CGContextGetFontSize(ctx));
449 CGContextShowGlyphsWithAdvances(ctx
,glyphs
,g_adv
,len
);
452 if(Free
) free(buffer
);
456 static void RQuartz_Rect(double x0
,double y0
,double x1
,double y1
,CTXDESC
) {
458 SET(RQUARTZ_FILL
|RQUARTZ_STROKE
|RQUARTZ_LINE
);
459 CGContextBeginPath(ctx
);
460 CGContextAddRect(ctx
,CGRectMake(x0
,y0
,x1
-x0
,y1
-y0
));
461 CGContextDrawPath(ctx
,kCGPathFillStroke
);
464 static void RQuartz_Circle(double x
,double y
,double r
,CTXDESC
) {
466 SET(RQUARTZ_FILL
|RQUARTZ_STROKE
|RQUARTZ_LINE
);
468 CGContextBeginPath(ctx
);
469 CGContextAddEllipseInRect(ctx
,CGRectMake(x
-r
,y
-r
,r2
,r2
));
470 CGContextDrawPath(ctx
,kCGPathFillStroke
);
473 static void RQuartz_Line(double x1
,double y1
,double x2
,double y2
,CTXDESC
) {
475 SET(RQUARTZ_STROKE
|RQUARTZ_LINE
);
476 CGContextBeginPath(ctx
);
477 CGContextMoveToPoint(ctx
,x1
,y1
);
478 CGContextAddLineToPoint(ctx
,x2
,y2
);
479 CGContextStrokePath(ctx
);
482 static void RQuartz_Polyline(int n
,double *x
,double *y
,CTXDESC
) {
486 SET(RQUARTZ_STROKE
|RQUARTZ_LINE
);
487 CGContextBeginPath(ctx
);
488 CGContextMoveToPoint(ctx
,x
[0],y
[0]);
489 for(i
=1;i
<n
;i
++) CGContextAddLineToPoint(ctx
,x
[i
],y
[i
]);
490 CGContextStrokePath(ctx
);
493 static void RQuartz_Polygon(int n
,double *x
,double *y
,CTXDESC
) {
497 SET(RQUARTZ_FILL
|RQUARTZ_STROKE
|RQUARTZ_LINE
);
498 CGContextBeginPath(ctx
);
499 CGContextMoveToPoint(ctx
,x
[0],y
[0]);
500 for(i
=1;i
<n
;i
++) CGContextAddLineToPoint(ctx
,x
[i
],y
[i
]);
501 CGContextClosePath(ctx
);
502 CGContextDrawPath(ctx
,kCGPathFillStroke
);
505 static void RQuartz_Mode(int mode
,DEVDESC
) {
510 CGContextRestoreGState(ctx
);
512 CGContextSynchronize(ctx
);
514 //Set the clipping rect for this operation.
517 CGContextRestoreGState(ctx
);
520 CGContextSaveGState(ctx
);
521 CGContextClipToRect(ctx
,xd
->clipRect
);
525 static void RQuartz_Hold(DEVDESC
) {
528 static void RQuartz_MetricInfo(int c
,R_GE_gcontext
*gc
,double *ascent
,double *descent
,double *width
,NewDevDesc
*dd
) {
534 CGFontRef font
= CGContextGetFont(ctx
);
535 float aScale
= (gc
->cex
*gc
->ps
*xd
->tscale
)/CGFontGetUnitsPerEm(font
);
540 CFStringRef str
= prepareText(gc
,dd
,text
,&buffer
,&Free
);
541 len
= CFStringGetLength(str
);
542 glyphs
= malloc(sizeof(CGGlyph
)*len
);
543 CGFontGetGlyphsForUnichars(font
,buffer
,glyphs
,len
);
544 int *advances
= malloc(sizeof(int)*len
);
545 CGRect
*bboxes
= malloc(sizeof(CGRect
)*len
);
546 CGFontGetGlyphAdvances(font
,glyphs
,len
,advances
);
547 CGFontGetGlyphBBoxes(font
,glyphs
,len
,bboxes
);
548 for(i
=0;i
<len
;i
++) *width
+= advances
[i
]*aScale
;
549 *ascent
= aScale
*(bboxes
[0].size
.height
+ bboxes
[0].origin
.y
);
550 *descent
= -aScale
*bboxes
[0].origin
.y
;
554 if(Free
) free(buffer
);
558 static Rboolean
RQuartz_Locator(double *x
,double *y
,DEVDESC
) {
561 if(NULL
== xd
->locatePoint
)
563 return xd
->locatePoint(xd
,xd
->userInfo
,x
,y
);