Text output at arbitrary angles, and more...
I finally sat down with the suggestion from John DeSoi -- that was a pretty good hint. But I ended up going directly from the plotting pane at hand, instead of chasing up to the interface and then back down to (hopefully) the same pane...vis... (let ((ns-view (slot-value (capi-internals:representation pane)
----------------------------------------------------------------------
'capi-cocoa-library::main-view)))
.... do stuff with ns-view ....)
That works just great. And so below you will find the Cocoa code (in C and Lisp) for producing text output at arbitrary angles, as well as code for copying a pane into the clipboard as PDF, and for saving the pane itself as a PDF file.
David McClain
Chief Technical Officer
Refined Audiometrics Laboratory
4391 N. Camino Ferreo
Tucson, AZ 85750
email: dbm@refined-audiometrics.com
phone: 1.520.390.3995
Skype: dbmcclain
// C Code for NSPane manipulations...
extern "C" {
#import <Cocoa/Cocoa.h>
};
static NSAutoreleasePool *_pool;
void pltstuff_init()
{
if (!_pool)
_pool = [[NSAutoreleasePool alloc] init];
}
void pltstuff_terminate(void)
{
[_pool release];
_pool = nil;
}
@interface NSFont (NSFontHiddenMethods)
- (NSGlyph)_defaultGlyphForChar:(unichar)theChar;
@end
typedef struct
{
float red;
float green;
float blue;
float alpha;
} colorStruct;
extern "C"
void pltstuff_add_label(NSView *view, char* text, float x, float y,
char* font_name, float font_size, colorStruct *color,
long justification, float angle)
{
pltstuff_init();
int i = 0;
NSFont *aFont = [NSFont fontWithName:[NSString stringWithCString:font_name]
size:font_size];
int strLen = strlen(text);
NSAffineTransform *aTransform = [NSAffineTransform transform];
NSBezierPath *tmpPath = [NSBezierPath bezierPath];
NSSize tmpSize;
NSPoint adjust = NSZeroPoint;
NSPoint pos = NSZeroPoint;
//
// appendBezierPathWithGlyph needs a valid context...
//
[view lockFocus];
//
// Create glyphs and convert to path
//
[tmpPath moveToPoint:pos];
for(i=0; i<strLen; i++)
{
NSGlyph theGlyph;
NSSize offset;
theGlyph = [aFont _defaultGlyphForChar:(text[i])];
offset = [aFont advancementForGlyph:theGlyph];
[tmpPath appendBezierPathWithGlyph:theGlyph inFont:aFont];
pos.x += offset.width;
pos.y += offset.height;
[tmpPath moveToPoint:pos];
}
tmpSize = [tmpPath bounds].size;
//
// Place the path according to position, angle and align
//
// hAlign:
adjust.x = -(float)(justification & 0x03)*0.5*tmpSize.width;
// vAlign:
switch (justification & 0x1C)
{
case 0x00:// AQTAlignMiddle: // align middle wrt *font size*
adjust.y = -([aFont descender] + [aFont capHeight])*0.5;
break;
case 0x04:// AQTAlignBaseline: // align baseline (do nothing)
break;
case 0x08:// AQTAlignBottom: // align bottom wrt *bounding box*
adjust.y = -[tmpPath bounds].origin.y;
break;
case 0x10:// AQTAlignTop: // align top wrt *bounding box*
adjust.y = -([tmpPath bounds].origin.y + tmpSize.height) ;
break;
default:
// default to align baseline (do nothing) in case of error
break;
}
[aTransform translateXBy:x yBy:y];
[aTransform rotateByDegrees:angle];
[aTransform translateXBy:adjust.x yBy:adjust.y];
[tmpPath transformUsingAffineTransform:aTransform];
[[NSColor colorWithCalibratedRed: (color->red)
green:(color->green)
blue: (color->blue)
alpha:(color->alpha)] set];
[tmpPath fill];
[view unlockFocus];
}
extern "C"
void pltstuff_copy_pdf_plot(NSView *view)
{
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard declareTypes:[NSArray arrayWithObjects:NSPDFPboardType, NSPostScriptPboardType, nil] owner:nil];
[pasteboard setData:[view dataWithPDFInsideRect:[view bounds]] forType:NSPDFPboardType];
[pasteboard setData:[view dataWithEPSInsideRect:[view bounds]] forType:NSPostScriptPboardType];
}
extern "C"
void pltstuff_save_pdf_plot(NSView *view, char *filename)
{
NSData *data;
NSString *fname;
fname = [[NSString stringWithCString: filename] stringByDeletingPathExtension];
data = [view dataWithPDFInsideRect: [view bounds]];
[data writeToFile: [fname stringByAppendingPathExtension:@"pdf"] atomically: NO];
}
// ------------------------------------------------------------------------------------------------------------
#Makefile info for producing a dylib called libLispPlotterStuff.dylib
plotterStuff: plotter-stuff.mm
$(GCC) -o libLispPlotterStuff.dylib \
plotter-stuff.mm \
-lstdc++ -framework AppKit
mv libLispPlotterStuff.dylib /usr/local/lib
;; ----------------------------------------------------------------------------------------------------------------
;; Lisp code for interfacing to the C library...
(defvar *plotter-stuff-lib* (fli:register-module "/usr/local/lib/libLispPlotterStuff.dylib"))
(fli:define-c-struct (color-struct (:foreign-name "colorStruct"))
(red :float)
(green :float)
(blue :float)
(alpha :float))
(fli:define-foreign-function (_add-label "pltstuff_add_label" :source)
((view :pointer)
(text (:reference-pass
(:ef-mb-string :limit 256)))
(x :float)
(y :float)
(font-name (:reference-pass
(:ef-mb-string :limit 256)))
(font-size :float)
(color (:pointer color-struct))
(just :long)
(angle :float))
:language :ansi-c
:result-type :void)
(defun fill-color-spec (color-struct color alpha)
(let ((rgb (if (keywordp color)
(color:get-color-translation color)
color)))
(setf (fli:foreign-slot-value color-struct 'red) (float (aref rgb 1) 1.0)
(fli:foreign-slot-value color-struct 'green) (float (aref rgb 2) 1.0)
(fli:foreign-slot-value color-struct 'blue) (float (aref rgb 3) 1.0)
(fli:foreign-slot-value color-struct 'alpha) (float alpha 1.0)
)))
(defun add-label (pane text x y
&key
(font "Times-Roman")
(font-size 12)
(color :black)
(alpha 1.0)
(x-alignment :left)
(y-alignment :baseline)
(angle 0.0))
(capi:apply-in-pane-process pane
(lambda ()
(fli:with-dynamic-foreign-objects ()
(let ((color-struct (fli:allocate-dynamic-foreign-object
:type 'color-struct))
(ns-view (slot-value (capi-internals:representation pane)
'capi-cocoa-library::main-view)))
(fill-color-spec color-struct color alpha)
(_add-label ns-view
text
(float x 1.0)
(float (- (gp:port-height pane) y) 1.0) ;; note NSView y-coords are inverted from pane y-coords
font (float font-size 1.0) color-struct
(+ (* 4 (ecase y-alignment
(:center 0)
(:baseline 1)
(:bottom 2)
(:top 4)))
(ecase x-alignment
(:left 0)
(:center 1)
(:right 2)))
(float angle 1.0))
))
)))
(fli:define-foreign-function (_copy-pdf-plot "pltstuff_copy_pdf_plot" :source)
((view :pointer))
:language :ansi-c
:result-type :void)
(defun copy-pdf-plot (pane)
(capi:apply-in-pane-process pane
(lambda ()
(let ((ns-view (slot-value (capi-internals:representation pane)
'capi-cocoa-library::main-view)))
(_copy-pdf-plot ns-view)))
))
(fli:define-foreign-function (_save-pdf-plot "pltstuff_save_pdf_plot" :source)
((view :pointer)
(filename (:reference-pass
(:ef-mb-string :limit 256))))
:language :ansi-c
:result-type :void)
(defun save-pdf-plot (pane filename)
(capi:apply-in-pane-process pane
(lambda ()
(let ((ns-view (slot-value (capi-internals:representation pane)
'capi-cocoa-library::main-view)))
(_save-pdf-plot ns-view filename)))
))