Lisp HUG Maillist Archive

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
web: http://www.refined-audiometrics.com
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)))
                              ))

Updated at: 2020-12-10 08:46 UTC