|
From: <jd...@us...> - 2008-12-09 03:51:40
|
Revision: 6531
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6531&view=rev
Author: jdh2358
Date: 2008-12-09 03:51:35 +0000 (Tue, 09 Dec 2008)
Log Message:
-----------
added mdehoon's native macosx patch
Modified Paths:
--------------
trunk/matplotlib/CHANGELOG
Added Paths:
-----------
trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py
trunk/matplotlib/src/_macosx.m
Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG 2008-12-09 03:49:11 UTC (rev 6530)
+++ trunk/matplotlib/CHANGELOG 2008-12-09 03:51:35 UTC (rev 6531)
@@ -1,4 +1,4 @@
-2008-12-08 Added mdehoon's native maxosx backend from sf patch 2179017
+2008-12-08 Added mdehoon's native macosx backend from sf patch 2179017
2008-12-08 Removed the prints in the set_*style commands. Return the
list of pprinted strings instead
Added: trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py (rev 0)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2008-12-09 03:51:35 UTC (rev 6531)
@@ -0,0 +1,415 @@
+from __future__ import division
+
+import os
+import numpy
+
+from matplotlib._pylab_helpers import Gcf
+from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\
+ FigureManagerBase, FigureCanvasBase, NavigationToolbar2
+from matplotlib.cbook import maxdict
+from matplotlib.figure import Figure
+from matplotlib.path import Path
+from matplotlib.mathtext import MathTextParser
+
+
+
+from matplotlib.widgets import SubplotTool
+
+import matplotlib
+from matplotlib.backends import _macosx
+
+def show():
+ """Show all the figures and enter the Cocoa mainloop.
+ This function will not return until all windows are closed or
+ the interpreter exits."""
+ # Having a Python-level function "show" wrapping the built-in
+ # function "show" in the _macosx extension module allows us to
+ # to add attributes to "show". This is something ipython does.
+ _macosx.show()
+
+class RendererMac(RendererBase):
+ """
+ The renderer handles drawing/rendering operations. Most of the renderer's
+ methods forwards the command to the renderer's graphics context. The
+ renderer does not wrap a C object and is written in pure Python.
+ """
+
+ texd = maxdict(50) # a cache of tex image rasters
+
+ def __init__(self, dpi, width, height):
+ RendererBase.__init__(self)
+ self.dpi = dpi
+ self.width = width
+ self.height = height
+ self.gc = GraphicsContextMac()
+ self.mathtext_parser = MathTextParser('MacOSX')
+
+ def set_width_height (self, width, height):
+ self.width, self.height = width, height
+
+ def draw_path(self, gc, path, transform, rgbFace=None):
+ path = transform.transform_path(path)
+ for points, code in path.iter_segments():
+ if code == Path.MOVETO:
+ gc.moveto(points)
+ elif code == Path.LINETO:
+ gc.lineto(points)
+ elif code == Path.CURVE3:
+ gc.curve3(points)
+ elif code == Path.CURVE4:
+ gc.curve4(points)
+ elif code == Path.CLOSEPOLY:
+ gc.closepoly()
+ if rgbFace is not None:
+ rgbFace = tuple(rgbFace)
+ gc.stroke(rgbFace)
+
+ def new_gc(self):
+ self.gc.reset()
+ return self.gc
+
+ def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None):
+ self.gc.set_clip_rectangle(bbox)
+ im.flipud_out()
+ nrows, ncols, data = im.as_rgba_str()
+ self.gc.draw_image(x, y, nrows, ncols, data)
+ im.flipud_out()
+
+ def draw_tex(self, gc, x, y, s, prop, angle):
+ # todo, handle props, angle, origins
+ size = prop.get_size_in_points()
+ texmanager = self.get_texmanager()
+ key = s, size, self.dpi, angle, texmanager.get_font_config()
+ im = self.texd.get(key) # Not sure what this does; just copied from backend_agg.py
+ if im is None:
+ Z = texmanager.get_grey(s, size, self.dpi)
+ Z = numpy.array(255.0 - Z * 255.0, numpy.uint8)
+
+ gc.draw_mathtext(x, y, angle, Z)
+
+ def _draw_mathtext(self, gc, x, y, s, prop, angle):
+ size = prop.get_size_in_points()
+ ox, oy, width, height, descent, image, used_characters = \
+ self.mathtext_parser.parse(s, self.dpi, prop)
+ gc.draw_mathtext(x, y, angle, 255 - image.as_array())
+
+ def draw_text(self, gc, x, y, s, prop, angle, ismath=False):
+ if ismath:
+ self._draw_mathtext(gc, x, y, s, prop, angle)
+ else:
+ family = prop.get_family()
+ size = prop.get_size_in_points()
+ weight = prop.get_weight()
+ style = prop.get_style()
+ gc.draw_text(x, y, unicode(s), family, size, weight, style, angle)
+
+ def get_text_width_height_descent(self, s, prop, ismath):
+ if ismath=='TeX':
+ # TODO: handle props
+ size = prop.get_size_in_points()
+ texmanager = self.get_texmanager()
+ Z = texmanager.get_grey(s, size, self.dpi)
+ m,n = Z.shape
+ # TODO: handle descent; This is based on backend_agg.py
+ return n, m, 0
+ if ismath:
+ ox, oy, width, height, descent, fonts, used_characters = \
+ self.mathtext_parser.parse(s, self.dpi, prop)
+ return width, height, descent
+ family = prop.get_family()
+ size = prop.get_size_in_points()
+ weight = prop.get_weight()
+ style = prop.get_style()
+ return self.gc.get_text_width_height_descent(unicode(s), family, size, weight, style)
+
+ def flipy(self):
+ return False
+
+ def points_to_pixels(self, points):
+ return points/72.0 * self.dpi
+
+ def option_image_nocomposite(self):
+ return True
+
+class GraphicsContextMac(_macosx.GraphicsContext, GraphicsContextBase):
+ """
+ The GraphicsContext wraps a Quartz graphics context. All methods
+ are implemented at the C-level in macosx.GraphicsContext. These
+ methods set drawing properties such as the line style, fill color,
+ etc. The actual drawing is done by the Renderer, which draws into
+ the GraphicsContext.
+ """
+ def __init__(self):
+ GraphicsContextBase.__init__(self)
+ _macosx.GraphicsContext.__init__(self)
+
+ def set_clip_rectangle(self, box):
+ GraphicsContextBase.set_clip_rectangle(self, box)
+ if not box: return
+ _macosx.GraphicsContext.set_clip_rectangle(self, box.bounds)
+
+ def set_clip_path(self, path):
+ GraphicsContextBase.set_clip_path(self, path)
+ if not path: return
+ path = path.get_fully_transformed_path()
+ for points, code in path.iter_segments():
+ if code == Path.MOVETO:
+ self.moveto(points)
+ elif code == Path.LINETO:
+ self.lineto(points)
+ elif code == Path.CURVE3:
+ self.curve3(points)
+ elif code == Path.CURVE4:
+ self.curve4(points)
+ elif code == Path.CLOSEPOLY:
+ self.closepoly()
+ self.clip_path()
+
+
+########################################################################
+#
+# The following functions and classes are for pylab and implement
+# window/figure managers, etc...
+#
+########################################################################
+
+def draw_if_interactive():
+ """
+ For performance reasons, we don't want to redraw the figure after
+ each draw command. Instead, we mark the figure as invalid, so that
+ it will be redrawn as soon as the event loop resumes via PyOS_InputHook.
+ This function should be called after each draw event, even if
+ matplotlib is not running interactively.
+ """
+ figManager = Gcf.get_active()
+ if figManager is not None:
+ figManager.canvas.invalidate()
+
+def new_figure_manager(num, *args, **kwargs):
+ """
+ Create a new figure manager instance
+ """
+ FigureClass = kwargs.pop('FigureClass', Figure)
+ figure = FigureClass(*args, **kwargs)
+ canvas = FigureCanvasMac(figure)
+ manager = FigureManagerMac(canvas, num)
+ return manager
+
+class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase):
+ """
+ The canvas the figure renders into. Calls the draw and print fig
+ methods, creates the renderers, etc...
+
+ Public attribute
+
+ figure - A Figure instance
+
+ Events such as button presses, mouse movements, and key presses
+ are handled in the C code and the base class methods
+ button_press_event, button_release_event, motion_notify_event,
+ key_press_event, and key_release_event are called from there.
+ """
+
+ def __init__(self, figure):
+ FigureCanvasBase.__init__(self, figure)
+ width, height = self.get_width_height()
+ self.renderer = RendererMac(figure.dpi, width, height)
+ _macosx.FigureCanvas.__init__(self, width, height)
+
+ def resize(self, width, height):
+ self.renderer.set_width_height(width, height)
+ dpi = self.figure.dpi
+ width /= dpi
+ height /= dpi
+ self.figure.set_size_inches(width, height)
+
+ def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w',
+ orientation='portrait', **kwargs):
+ if dpi is None: dpi = matplotlib.rcParams['savefig.dpi']
+ filename = unicode(filename)
+ root, ext = os.path.splitext(filename)
+ ext = ext[1:].lower()
+ if not ext:
+ ext = "png"
+ filename = root + "." + ext
+ if ext=="jpg": ext = "jpeg"
+
+ # save the figure settings
+ origfacecolor = self.figure.get_facecolor()
+ origedgecolor = self.figure.get_edgecolor()
+
+ # set the new parameters
+ self.figure.set_facecolor(facecolor)
+ self.figure.set_edgecolor(edgecolor)
+
+ if ext in ('jpeg', 'png', 'tiff', 'gif', 'bmp'):
+ width, height = self.figure.get_size_inches()
+ width, height = width*dpi, height*dpi
+ self.write_bitmap(filename, width, height)
+ elif ext == 'pdf':
+ self.write_pdf(filename)
+ elif ext in ('ps', 'eps'):
+ from backend_ps import FigureCanvasPS
+ # Postscript backend changes figure.dpi, but doesn't change it back
+ origDPI = self.figure.dpi
+ fc = self.switch_backends(FigureCanvasPS)
+ fc.print_figure(filename, dpi, facecolor, edgecolor,
+ orientation, **kwargs)
+ self.figure.dpi = origDPI
+ self.figure.set_canvas(self)
+ elif ext=='svg':
+ from backend_svg import FigureCanvasSVG
+ fc = self.switch_backends(FigureCanvasSVG)
+ fc.print_figure(filename, dpi, facecolor, edgecolor,
+ orientation, **kwargs)
+ self.figure.set_canvas(self)
+ else:
+ raise ValueError("Figure format not available (extension %s)" % ext)
+
+ # restore original figure settings
+ self.figure.set_facecolor(origfacecolor)
+ self.figure.set_edgecolor(origedgecolor)
+
+
+
+class FigureManagerMac(_macosx.FigureManager, FigureManagerBase):
+ """
+ Wrap everything up into a window for the pylab interface
+ """
+ def __init__(self, canvas, num):
+ FigureManagerBase.__init__(self, canvas, num)
+ title = "Figure %d" % num
+ _macosx.FigureManager.__init__(self, canvas, title)
+ if matplotlib.rcParams['toolbar']=='classic':
+ self.toolbar = NavigationToolbarMac(canvas)
+ elif matplotlib.rcParams['toolbar']=='toolbar2':
+ self.toolbar = NavigationToolbar2Mac(canvas)
+ else:
+ self.toolbar = None
+ if self.toolbar is not None:
+ self.toolbar.update()
+
+ def notify_axes_change(fig):
+ 'this will be called whenever the current axes is changed'
+ if self.toolbar != None: self.toolbar.update()
+ self.canvas.figure.add_axobserver(notify_axes_change)
+
+ # This is ugly, but this is what tkagg and gtk are doing.
+ # It is needed to get ginput() working.
+ self.canvas.figure.show = lambda *args: self.show()
+
+ def show(self):
+ self.canvas.draw()
+
+ def close(self):
+ Gcf.destroy(self.num)
+
+class NavigationToolbarMac(_macosx.NavigationToolbar):
+
+ def __init__(self, canvas):
+ self.canvas = canvas
+ basedir = os.path.join(matplotlib.rcParams['datapath'], "images")
+ images = {}
+ for imagename in ("stock_left",
+ "stock_right",
+ "stock_up",
+ "stock_down",
+ "stock_zoom-in",
+ "stock_zoom-out",
+ "stock_save_as"):
+ filename = os.path.join(basedir, imagename+".ppm")
+ images[imagename] = self._read_ppm_image(filename)
+ _macosx.NavigationToolbar.__init__(self, images)
+ self.message = None
+
+ def _read_ppm_image(self, filename):
+ data = ""
+ imagefile = open(filename)
+ for line in imagefile:
+ if "#" in line:
+ i = line.index("#")
+ line = line[:i] + "\n"
+ data += line
+ imagefile.close()
+ magic, width, height, maxcolor, imagedata = data.split(None, 4)
+ width, height = int(width), int(height)
+ assert magic=="P6"
+ assert len(imagedata)==width*height*3 # 3 colors in RGB
+ return (width, height, imagedata)
+
+ def panx(self, direction):
+ axes = self.canvas.figure.axes
+ selected = self.get_active()
+ for i in selected:
+ axes[i].xaxis.pan(direction)
+ self.canvas.invalidate()
+
+ def pany(self, direction):
+ axes = self.canvas.figure.axes
+ selected = self.get_active()
+ for i in selected:
+ axes[i].yaxis.pan(direction)
+ self.canvas.invalidate()
+
+ def zoomx(self, direction):
+ axes = self.canvas.figure.axes
+ selected = self.get_active()
+ for i in selected:
+ axes[i].xaxis.zoom(direction)
+ self.canvas.invalidate()
+
+ def zoomy(self, direction):
+ axes = self.canvas.figure.axes
+ selected = self.get_active()
+ for i in selected:
+ axes[i].yaxis.zoom(direction)
+ self.canvas.invalidate()
+
+ def save_figure(self):
+ filename = _macosx.choose_save_file('Save the figure')
+ if filename is None: # Cancel
+ return
+ self.canvas.print_figure(filename)
+
+class NavigationToolbar2Mac(_macosx.NavigationToolbar2, NavigationToolbar2):
+
+ def __init__(self, canvas):
+ NavigationToolbar2.__init__(self, canvas)
+
+ def _init_toolbar(self):
+ basedir = os.path.join(matplotlib.rcParams['datapath'], "images")
+ _macosx.NavigationToolbar2.__init__(self, basedir)
+
+ def draw_rubberband(self, event, x0, y0, x1, y1):
+ self.canvas.set_rubberband(x0, y0, x1, y1)
+
+ def release(self, event):
+ self.canvas.remove_rubberband()
+
+ def set_cursor(self, cursor):
+ _macosx.set_cursor(cursor)
+
+ def save_figure(self):
+ filename = _macosx.choose_save_file('Save the figure')
+ if filename is None: # Cancel
+ return
+ self.canvas.print_figure(filename)
+
+ def prepare_configure_subplots(self):
+ toolfig = Figure(figsize=(6,3))
+ canvas = FigureCanvasMac(toolfig)
+ toolfig.subplots_adjust(top=0.9)
+ tool = SubplotTool(self.canvas.figure, toolfig)
+ return canvas
+
+ def set_message(self, message):
+ _macosx.NavigationToolbar2.set_message(self, message.encode('utf-8'))
+
+########################################################################
+#
+# Now just provide the standard names that backend.__init__ is expecting
+#
+########################################################################
+
+
+FigureManager = FigureManagerMac
Added: trunk/matplotlib/src/_macosx.m
===================================================================
--- trunk/matplotlib/src/_macosx.m (rev 0)
+++ trunk/matplotlib/src/_macosx.m 2008-12-09 03:51:35 UTC (rev 6531)
@@ -0,0 +1,3773 @@
+#include <Cocoa/Cocoa.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <sys/socket.h>
+#include <Python.h>
+#include "numpy/arrayobject.h"
+
+static int nwin = 0;
+
+
+/* Varius NSApplicationDefined event subtypes */
+#define STDIN_READY 0
+#define SIGINT_CALLED 1
+#define STOP_EVENT_LOOP 2
+#define WINDOW_CLOSING 3
+
+/* -------------------------- Helper function ---------------------------- */
+
+static void stdin_ready(CFReadStreamRef readStream, CFStreamEventType eventType, void* context)
+{
+ NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined
+ location: NSZeroPoint
+ modifierFlags: 0
+ timestamp: 0.0
+ windowNumber: 0
+ context: nil
+ subtype: STDIN_READY
+ data1: 0
+ data2: 0];
+ [NSApp postEvent: event atStart: true];
+}
+
+static int sigint_fd = -1;
+
+static void _sigint_handler(int sig)
+{
+ const char c = 'i';
+ write(sigint_fd, &c, 1);
+}
+
+static void _callback(CFSocketRef s,
+ CFSocketCallBackType type,
+ CFDataRef address,
+ const void * data,
+ void *info)
+{
+ char c;
+ CFSocketNativeHandle handle = CFSocketGetNative(s);
+ read(handle, &c, 1);
+ NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined
+ location: NSZeroPoint
+ modifierFlags: 0
+ timestamp: 0.0
+ windowNumber: 0
+ context: nil
+ subtype: SIGINT_CALLED
+ data1: 0
+ data2: 0];
+ [NSApp postEvent: event atStart: true];
+}
+
+static int wait_for_stdin(void)
+{
+ const UInt8 buffer[] = "/dev/fd/0";
+ const CFIndex n = (CFIndex)strlen((char*)buffer);
+ CFRunLoopRef runloop = CFRunLoopGetCurrent();
+ CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ buffer,
+ n,
+ false);
+ CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault,
+ url);
+ CFRelease(url);
+
+ CFReadStreamOpen(stream);
+ if (!CFReadStreamHasBytesAvailable(stream))
+ /* This is possible because of how PyOS_InputHook is called from Python */
+ {
+ int error;
+ int interrupted = 0;
+ int channel[2];
+ CFSocketRef sigint_socket = NULL;
+ PyOS_sighandler_t py_sigint_handler = NULL;
+ CFStreamClientContext clientContext = {0, NULL, NULL, NULL, NULL};
+ CFReadStreamSetClient(stream,
+ kCFStreamEventHasBytesAvailable,
+ stdin_ready,
+ &clientContext);
+ CFReadStreamScheduleWithRunLoop(stream, runloop, kCFRunLoopCommonModes);
+ error = pipe(channel);
+ if (error==0)
+ {
+ fcntl(channel[1], F_SETFL, O_WRONLY | O_NONBLOCK);
+
+ sigint_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
+ channel[0],
+ kCFSocketReadCallBack,
+ _callback,
+ NULL);
+ if (sigint_socket)
+ {
+ CFRunLoopSourceRef source;
+ source = CFSocketCreateRunLoopSource(kCFAllocatorDefault,
+ sigint_socket,
+ 0);
+ CFRelease(sigint_socket);
+ if (source)
+ {
+ CFRunLoopAddSource(runloop, source, kCFRunLoopDefaultMode);
+ CFRelease(source);
+ sigint_fd = channel[1];
+ py_sigint_handler = PyOS_setsig(SIGINT, _sigint_handler);
+ }
+ }
+ else
+ close(channel[0]);
+ }
+
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ NSDate* date = [NSDate distantFuture];
+ while (true)
+ { NSEvent* event = [NSApp nextEventMatchingMask: NSAnyEventMask
+ untilDate: date
+ inMode: NSDefaultRunLoopMode
+ dequeue: YES];
+ if (!event) break; /* No windows open */
+ if ([event type]==NSApplicationDefined)
+ { short subtype = [event subtype];
+ if (subtype==STDIN_READY) break;
+ if (subtype==SIGINT_CALLED)
+ { interrupted = true;
+ break;
+ }
+ }
+ [NSApp sendEvent: event];
+ }
+ [pool release];
+
+ if (py_sigint_handler) PyOS_setsig(SIGINT, py_sigint_handler);
+ CFReadStreamUnscheduleFromRunLoop(stream,
+ runloop,
+ kCFRunLoopCommonModes);
+ if (sigint_socket) CFSocketInvalidate(sigint_socket);
+ if (error==0) close(channel[1]);
+ if (interrupted) raise(SIGINT);
+ }
+ CFReadStreamClose(stream);
+ return 1;
+}
+
+static char show__doc__[] = "Show all the figures and enter the main loop.\nThis function does not return until all Matplotlib windows are closed,\nand is normally not needed in interactive sessions.";
+
+/* ---------------------------- Cocoa classes ---------------------------- */
+
+
+@interface Window : NSWindow
+{ PyObject* manager;
+}
+- (Window*)initWithContentRect:(NSRect)rect styleMask:(unsigned int)mask backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation withManager: (PyObject*)theManager;
+- (BOOL)closeButtonPressed;
+- (void)close;
+- (void)dealloc;
+@end
+
+@interface ToolWindow : NSWindow
+{
+}
+- (ToolWindow*)initWithContentRect:(NSRect)rect master:(NSWindow*)window;
+- (void)masterCloses:(NSNotification*)notification;
+- (void)close;
+@end
+
+@interface View : NSView
+{ PyObject* canvas;
+ NSRect rubberband;
+}
+- (void)dealloc;
+- (void)drawRect:(NSRect)rect;
+- (void)windowDidResize:(NSNotification*)notification;
+- (View*)initWithFrame:(NSRect)rect canvas:(PyObject*)fc;
+- (BOOL)windowShouldClose:(NSNotification*)notification;
+- (BOOL)isFlipped;
+- (void)mouseDown:(NSEvent*)event;
+- (void)mouseUp:(NSEvent*)event;
+- (void)mouseDragged:(NSEvent*)event;
+- (void)mouseMoved:(NSEvent*)event;
+- (void)setRubberband:(NSRect)rect;
+- (void)removeRubberband;
+- (const char*)convertKeyEvent:(NSEvent*)event;
+- (void)keyDown:(NSEvent*)event;
+- (void)keyUp:(NSEvent*)event;
+- (void)scrollWheel:(NSEvent *)event;
+- (void)flagsChanged:(NSEvent*)event;
+@end
+
+@interface ScrollableButton : NSButton
+{
+ SEL scrollWheelUpAction;
+ SEL scrollWheelDownAction;
+}
+- (void)setScrollWheelUpAction:(SEL)action;
+- (void)setScrollWheelDownAction:(SEL)action;
+- (void)scrollWheel:(NSEvent *)event;
+@end
+
+@interface MenuItem: NSMenuItem
+{ int index;
+}
++ (MenuItem*)menuItemWithTitle:(NSString*)title;
++ (MenuItem*)menuItemSelectAll;
++ (MenuItem*)menuItemInvertAll;
++ (MenuItem*)menuItemForAxis:(int)i;
+- (void)toggle:(id)sender;
+- (void)selectAll:(id)sender;
+- (void)invertAll:(id)sender;
+- (int)index;
+@end
+
+/* ---------------------------- Python classes ---------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ CGContextRef cr;
+ PyObject* converter; /* Convert color specifications to r,g,b triples */
+ CGPatternRef pattern; /* For drawing hatches */
+ ATSUStyle style; /* For drawing Unicode strings with ATSUI */
+ ATSUTextLayout layout; /* For drawing Unicode strings with ATSUI */
+} GraphicsContext;
+
+static PyObject*
+GraphicsContext_new(PyTypeObject* type, PyObject *args, PyObject *kwds)
+{
+ OSStatus status;
+
+ GraphicsContext* self = (GraphicsContext*)type->tp_alloc(type, 0);
+ if (!self) return NULL;
+ self->cr = NULL;
+ PyObject* module = PyImport_AddModule("matplotlib.colors");
+ if (!module) return NULL;
+ PyObject* dict = PyObject_GetAttrString(module, "__dict__");
+ if (!dict) return NULL;
+ PyObject* colorConverter = PyDict_GetItemString(dict, "colorConverter");
+ Py_DECREF(dict);
+ if (!colorConverter)
+ {
+ PyErr_SetString(PyExc_KeyError,
+ "failed to find colorConverter in matplotlib.colors");
+ return NULL;
+ }
+ self->converter = PyObject_GetAttrString(colorConverter, "to_rgb");
+ if (!self->converter) return NULL;
+
+ self->pattern = NULL;
+
+ status = ATSUCreateStyle(&self->style);
+ if (status!=noErr)
+ {
+ Py_DECREF(self->converter);
+ PyErr_SetString(PyExc_RuntimeError, "ATSUCreateStyle failed");
+ return NULL;
+ }
+
+ status = ATSUCreateTextLayout(&self->layout);
+ if (status!=noErr)
+ {
+ Py_DECREF(self->converter);
+ status = ATSUDisposeStyle(self->style);
+ if (status!=noErr)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1);
+ PyErr_SetString(PyExc_RuntimeError, "ATSUCreateTextLayout failed");
+ return NULL;
+ }
+
+ return (PyObject*) self;
+}
+
+static void
+GraphicsContext_dealloc(GraphicsContext *self)
+{
+ Py_DECREF(self->converter);
+
+ if (self->pattern) CGPatternRelease(self->pattern);
+
+ OSStatus status;
+
+ status = ATSUDisposeStyle(self->style);
+ if (status!=noErr)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeStyle failed", 1);
+
+ status = ATSUDisposeTextLayout(self->layout);
+ if (status!=noErr)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeTextLayout failed", 1);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject*
+GraphicsContext_repr(GraphicsContext* self)
+{
+ return PyString_FromFormat("GraphicsContext object %p wrapping the Quartz 2D graphics context %p", self, self->cr);
+}
+
+static PyObject*
+GraphicsContext_reset (GraphicsContext* self)
+{
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextRestoreGState(cr);
+ CGContextSaveGState(cr);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_alpha (GraphicsContext* self, PyObject* args)
+{
+ float alpha;
+ if (!PyArg_ParseTuple(args, "f", &alpha)) return NULL;
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextSetAlpha(cr, alpha);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_antialiased (GraphicsContext* self, PyObject* args)
+{
+ int shouldAntialias;
+ if (!PyArg_ParseTuple(args, "i", &shouldAntialias)) return NULL;
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextSetShouldAntialias(cr, shouldAntialias);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_capstyle (GraphicsContext* self, PyObject* args)
+{
+ char* string;
+ CGLineCap cap;
+
+ if (!PyArg_ParseTuple(args, "s", &string)) return NULL;
+
+ if (!strcmp(string, "butt")) cap = kCGLineCapButt;
+ else if (!strcmp(string, "round")) cap = kCGLineCapRound;
+ else if (!strcmp(string, "projecting")) cap = kCGLineCapSquare;
+ else
+ {
+ PyErr_SetString(PyExc_ValueError,
+ "capstyle should be 'butt', 'round', or 'projecting'");
+ return NULL;
+ }
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextSetLineCap(cr, cap);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_clip_rectangle (GraphicsContext* self, PyObject* args)
+{
+ CGRect rect;
+ float x, y, width, height;
+ if (!PyArg_ParseTuple(args, "(ffff)", &x, &y, &width, &height)) return NULL;
+
+ rect.origin.x = x;
+ rect.origin.y = y;
+ rect.size.width = width;
+ rect.size.height = height;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ CGContextClipToRect(cr, rect);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_clip_path (GraphicsContext* self)
+{
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextRestoreGState(cr);
+ CGContextSaveGState(cr);
+ CGContextClip(cr);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_dashes (GraphicsContext* self, PyObject* args)
+{
+ float phase = 0.0;
+ PyObject* offset;
+ PyObject* dashes;
+
+ if (!PyArg_ParseTuple(args, "OO", &offset, &dashes)) return NULL;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if (offset!=Py_None)
+ {
+ if (PyFloat_Check(offset)) phase = PyFloat_AsDouble(offset);
+ else if (PyInt_Check(offset)) phase = PyInt_AsLong(offset);
+ else
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "offset should be a floating point value");
+ return NULL;
+ }
+ }
+
+ if (dashes!=Py_None)
+ {
+ if (PyList_Check(dashes)) dashes = PyList_AsTuple(dashes);
+ else if (PyTuple_Check(dashes)) Py_INCREF(dashes);
+ else
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "dashes should be a tuple or a list");
+ return NULL;
+ }
+ int n = PyTuple_GET_SIZE(dashes);
+ int i;
+ float* lengths = malloc(n*sizeof(float));
+ if(!lengths)
+ {
+ PyErr_SetString(PyExc_MemoryError, "Failed to store dashes");
+ Py_DECREF(dashes);
+ return NULL;
+ }
+ for (i = 0; i < n; i++)
+ {
+ PyObject* value = PyTuple_GET_ITEM(dashes, i);
+ if (PyFloat_Check(value))
+ lengths[i] = (float) PyFloat_AS_DOUBLE(value);
+ else if (PyInt_Check(value))
+ lengths[i] = (float) PyInt_AS_LONG(value);
+ else break;
+ }
+ Py_DECREF(dashes);
+ if (i < n) /* break encountered */
+ {
+ free(lengths);
+ PyErr_SetString(PyExc_TypeError, "Failed to read dashes");
+ return NULL;
+ }
+ CGContextSetLineDash(cr, phase, lengths, n);
+ free(lengths);
+ }
+ else
+ CGContextSetLineDash(cr, phase, NULL, 0);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_foreground(GraphicsContext* self, PyObject* args, PyObject* keywords)
+{ float r, g, b;
+ PyObject* fg;
+ int isRGB = 0;
+ static char* kwlist[] = {"fg", "isRGB", NULL};
+ if(!PyArg_ParseTupleAndKeywords(args, keywords, "O|i", kwlist,
+ &fg, &isRGB)) return NULL;
+ if (isRGB)
+ {
+ if(!PyArg_ParseTuple(fg, "fff", &r, &g, &b)) return NULL;
+ }
+ else
+ { fg = PyObject_CallFunctionObjArgs(self->converter, fg, NULL);
+ if(!fg) return NULL;
+ if(!PyArg_ParseTuple(fg, "fff", &r, &g, &b)) return NULL;
+ Py_DECREF(fg);
+ }
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ CGContextSetRGBStrokeColor(cr, r, g, b, 1.0);
+ CGContextSetRGBFillColor(cr, r, g, b, 1.0);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_graylevel(GraphicsContext* self, PyObject* args)
+{ float gray;
+ if(!PyArg_ParseTuple(args, "f", &gray)) return NULL;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ CGContextSetGrayStrokeColor(cr, gray, 1.0);
+ CGContextSetGrayFillColor(cr, gray, 1.0);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static void drawHatch (void *info, CGContextRef cr)
+{
+ int i;
+
+ PyObject* string = (PyObject*)info;
+ char* hatches = PyString_AS_STRING(string);
+
+ int frequency[4] = {0, 0, 0, 0};
+ float position, distance;
+
+ const float size = 12.0;
+ const int n = strlen(hatches);
+
+ for (i = 0; i < n; i++)
+ {
+ switch(hatches[i])
+ {
+ case '/': frequency[3]++; break;
+ case '\\': frequency[2]++; break;
+ case '|': frequency[1]++; break;
+ case '-': frequency[0]++; break;
+ case '+': frequency[0]++; frequency[1]++; break;
+ case 'x': frequency[2]++; frequency[3]++; break;
+ }
+ }
+
+ distance = size / frequency[0];
+ position = distance / 2.0;
+ for (i = 0; i < frequency[0]; i++, position += distance)
+ {
+ CGContextMoveToPoint(cr, 0.0, position);
+ CGContextAddLineToPoint(cr, size, position);
+ }
+ distance = size / frequency[1];
+ position = distance / 2.0;
+ for (i = 0; i < frequency[1]; i++, position += distance)
+ {
+ CGContextMoveToPoint(cr, position, 0.0);
+ CGContextAddLineToPoint(cr, position, size);
+ }
+ distance = size / frequency[2];
+ position = distance / 2.0;
+ for (i = 0; i < frequency[2]; i++, position += distance)
+ {
+ CGContextMoveToPoint(cr, position, 0.0);
+ CGContextAddLineToPoint(cr, 0.0, position);
+ CGContextMoveToPoint(cr, position, size);
+ CGContextAddLineToPoint(cr, size, position);
+ }
+ distance = size / frequency[3];
+ position = distance / 2.0;
+ for (i = 0; i < frequency[3]; i++, position += distance)
+ {
+ CGContextMoveToPoint(cr, position, 0.0);
+ CGContextAddLineToPoint(cr, size, size-position);
+ CGContextMoveToPoint(cr, position, size);
+ CGContextAddLineToPoint(cr, 0.0, size-position);
+ }
+ CGContextSetLineWidth(cr, 2.0);
+ CGContextSetLineCap(cr, kCGLineCapSquare);
+ CGContextStrokePath(cr);
+
+ Py_DECREF(string);
+}
+
+static PyObject*
+GraphicsContext_set_hatch(GraphicsContext* self, PyObject* args)
+{ PyObject* hatches;
+
+ const float size = 12.0;
+ static const CGPatternCallbacks callbacks = {0, &drawHatch, NULL};
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "O", &hatches)) return NULL;
+ if(!PyString_Check(hatches)) return NULL;
+
+ Py_INCREF(hatches);
+
+ CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
+ CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(baseSpace);
+ CGColorSpaceRelease(baseSpace);
+ CGContextSetFillColorSpace(cr, patternSpace);
+ CGColorSpaceRelease(patternSpace);
+
+ self->pattern = CGPatternCreate((void*)hatches,
+ CGRectMake(0, 0, size, size),
+ CGAffineTransformIdentity, size, size,
+ kCGPatternTilingNoDistortion,
+ false,
+ &callbacks);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_linewidth (GraphicsContext* self, PyObject* args)
+{
+ float width;
+ if (!PyArg_ParseTuple(args, "f", &width)) return NULL;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ CGContextSetLineWidth(cr, width);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_set_joinstyle(GraphicsContext* self, PyObject* args)
+{ char* string;
+ CGLineJoin join;
+
+ if (!PyArg_ParseTuple(args, "s", &string)) return NULL;
+
+ if (!strcmp(string, "miter")) join = kCGLineJoinMiter;
+ else if (!strcmp(string, "round")) join = kCGLineJoinRound;
+ else if (!strcmp(string, "bevel")) join = kCGLineJoinBevel;
+ else
+ {
+ PyErr_SetString(PyExc_ValueError,
+ "joinstyle should be 'miter', 'round', or 'bevel'");
+ return NULL;
+ }
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextSetLineJoin(cr, join);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_moveto(GraphicsContext* self, PyObject* args)
+{
+ float x;
+ float y;
+
+ if(!PyArg_ParseTuple(args, "(ff)", &x, &y)) return NULL;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextMoveToPoint(cr, x, y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_lineto(GraphicsContext* self, PyObject* args)
+{
+ float x;
+ float y;
+
+ if(!PyArg_ParseTuple(args, "(ff)", &x, &y)) return NULL;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+ CGContextAddLineToPoint(cr, x, y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_curve3(GraphicsContext* self, PyObject* args)
+{
+ float cpx;
+ float cpy;
+ float x;
+ float y;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "(ffff)", &cpx,
+ &cpy,
+ &x,
+ &y)) return NULL;
+
+ CGContextAddQuadCurveToPoint(cr, cpx, cpy, x, y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_curve4 (GraphicsContext* self, PyObject* args)
+{
+ float cp1x;
+ float cp1y;
+ float cp2x;
+ float cp2y;
+ float x;
+ float y;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "(ffffff)", &cp1x,
+ &cp1y,
+ &cp2x,
+ &cp2y,
+ &x,
+ &y)) return NULL;
+
+ CGContextAddCurveToPoint(cr, cp1x, cp1y, cp2x, cp2y, x, y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_closepoly (GraphicsContext* self)
+{
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ CGContextClosePath(cr);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_stroke (GraphicsContext* self, PyObject* args)
+{
+ PyObject* color;
+ CGContextRef cr = self->cr;
+
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "O", &color)) return NULL;
+
+ if(color!=Py_None)
+ {
+ float r, g, b;
+ if(!PyArg_ParseTuple(color, "fff", &r, &g, &b)) return NULL;
+ if(self->pattern)
+ {
+ float components[4];
+ components[0] = r;
+ components[1] = g;
+ components[2] = b;
+ components[3] = 1.0;
+ CGContextSetFillPattern(cr, self->pattern, components);
+ CGPatternRelease (self->pattern);
+ self->pattern = nil;
+ }
+ else CGContextSetRGBFillColor(cr, r, g, b, 1.0);
+ CGContextDrawPath(cr, kCGPathFillStroke);
+ }
+ else CGContextStrokePath(cr);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static ATSFontRef
+setfont(CGContextRef cr, PyObject* family, float size, const char weight[],
+ const char style[])
+{
+#define NMAP 40
+#define NFONT 31
+ int i, j, n;
+ const char* temp;
+ const char* name = "Times-Roman";
+ CFStringRef string;
+ ATSFontRef atsfont = 0;
+
+ const int k = (strcmp(style, "italic") ? 0 : 2)
+ + (strcmp(weight, "bold") ? 0 : 1);
+
+ struct {char* name; int index;} map[NMAP] = {
+ {"New Century Schoolbook", 0},
+ {"Century Schoolbook L", 0},
+ {"Utopia", 1},
+ {"ITC Bookman", 2},
+ {"Bookman", 2},
+ {"Bitstream Vera Serif", 3},
+ {"Nimbus Roman No9 L", 4},
+ {"Times New Roman", 5},
+ {"Times", 6},
+ {"Palatino", 7},
+ {"Charter", 8},
+ {"serif", 0},
+ {"Lucida Grande", 9},
+ {"Verdana", 10},
+ {"Geneva", 11},
+ {"Lucida", 12},
+ {"Bitstream Vera Sans", 13},
+ {"Arial", 14},
+ {"Helvetica", 15},
+ {"Avant Garde", 16},
+ {"sans-serif", 10},
+ {"Apple Chancery", 17},
+ {"Textile", 18},
+ {"Zapf Chancery", 19},
+ {"Sand", 20},
+ {"cursive", 17},
+ {"Comic Sans MS", 21},
+ {"Chicago", 22},
+ {"Charcoal", 23},
+ {"Impact", 24},
+ {"Western", 25},
+ {"fantasy", 21},
+ {"Andale Mono", 26},
+ {"Bitstream Vera Sans Mono", 27},
+ {"Nimbus Mono L", 28},
+ {"Courier", 29},
+ {"Courier New", 30},
+ {"Fixed", 30},
+ {"Terminal", 30},
+ {"monospace", 30},
+ };
+
+ const char* psnames[NFONT][4] = {
+ {"CenturySchoolbook", /* 0 */
+ "CenturySchoolbook-Bold",
+ "CenturySchoolbook-Italic",
+ "CenturySchoolbook-BoldItalic"},
+ {"Utopia", /* 1 */
+ "Utopia-Bold",
+ "Utopia-Italic",
+ "Utopia-BoldItalic"},
+ {"Bookman-Light", /* 2 */
+ "Bookman-Bold",
+ "Bookman-LightItalic",
+ "Bookman-BoldItalic"},
+ {"BitstreamVeraSerif-Roman", /* 3 */
+ "BitstreamVeraSerif-Bold",
+ "",
+ ""},
+ {"NimbusRomNo9L-Reg", /* 4 */
+ "NimbusRomNo9T-Bol",
+ "NimbusRomNo9L-RegIta",
+ "NimbusRomNo9T-BolIta"},
+ {"TimesNewRomanPSMT", /* 5 */
+ "TimesNewRomanPS-BoldMT",
+ "TimesNewRomanPS-ItalicMT",
+ "TimesNewRomanPS-BoldItalicMT"},
+ {"Times-Roman", /* 6 */
+ "Times-Bold",
+ "Times-Italic",
+ "Times-BoldItalic"},
+ {"Palatino-Roman", /* 7 */
+ "Palatino-Bold",
+ "Palatino-Italic",
+ "Palatino-BoldItalic"},
+ {"CharterBT-Roman", /* 8 */
+ "CharterBT-Bold",
+ "CharterBT-Italic",
+ "CharterBT-BoldItalic"},
+ {"LucidaGrande", /* 9 */
+ "LucidaGrande-Bold",
+ "",
+ ""},
+ {"Verdana", /* 10 */
+ "Verdana-Bold",
+ "Verdana-Italic",
+ "Verdana-BoldItalic"},
+ {"Geneva", /* 11 */
+ "",
+ "",
+ ""},
+ {"LucidaSans", /* 12 */
+ "LucidaSans-Demi",
+ "LucidaSans-Italic",
+ "LucidaSans-DemiItalic"},
+ {"BitstreamVeraSans-Roman", /* 13 */
+ "BitstreamVeraSans-Bold",
+ "BitstreamVeraSans-Oblique",
+ "BitstreamVeraSans-BoldOblique"},
+ {"ArialMT", /* 14 */
+ "Arial-BoldMT",
+ "Arial-ItalicMT",
+ "Arial-BoldItalicMT"},
+ {"Helvetica", /* 15 */
+ "Helvetica-Bold",
+ "",
+ ""},
+ {"AvantGardeITC-Book", /* 16 */
+ "AvantGardeITC-Demi",
+ "AvantGardeITC-BookOblique",
+ "AvantGardeITC-DemiOblique"},
+ {"Apple-Chancery", /* 17 */
+ "",
+ "",
+ ""},
+ {"TextileRegular", /* 18 */
+ "",
+ "",
+ ""},
+ {"ZapfChancery-Roman", /* 19 */
+ "ZapfChancery-Bold",
+ "ZapfChancery-Italic",
+ "ZapfChancery-MediumItalic"},
+ {"SandRegular", /* 20 */
+ "",
+ "",
+ ""},
+ {"ComicSansMS", /* 21 */
+ "ComicSansMS-Bold",
+ "",
+ ""},
+ {"Chicago", /* 22 */
+ "",
+ "",
+ ""},
+ {"Charcoal", /* 23 */
+ "",
+ "",
+ ""},
+ {"Impact", /* 24 */
+ "",
+ "",
+ ""},
+ {"Playbill", /* 25 */
+ "",
+ "",
+ ""},
+ {"AndaleMono", /* 26 */
+ "",
+ "",
+ ""},
+ {"BitstreamVeraSansMono-Roman", /* 27 */
+ "BitstreamVeraSansMono-Bold",
+ "BitstreamVeraSansMono-Oblique",
+ "BitstreamVeraSansMono-BoldOb"},
+ {"NimbusMonL-Reg", /* 28 */
+ "NimbusMonL-Bol",
+ "NimbusMonL-RegObl",
+ "NimbusMonL-BolObl"},
+ {"Courier", /* 29 */
+ "Courier-Bold",
+ "",
+ ""},
+ {"CourierNewPS", /* 30 */
+ "CourierNewPS-BoldMT",
+ "CourierNewPS-ItalicMT",
+ "CourierNewPS-Bold-ItalicMT"},
+ };
+
+ if(!PyList_Check(family)) return 0;
+ n = PyList_GET_SIZE(family);
+
+ for (i = 0; i < n; i++)
+ {
+ PyObject* item = PyList_GET_ITEM(family, i);
+ if(!PyString_Check(item)) return 0;
+ temp = PyString_AS_STRING(item);
+ for (j = 0; j < NMAP; j++)
+ { if (!strcmp(map[j].name, temp))
+ { temp = psnames[map[j].index][k];
+ break;
+ }
+ }
+ /* If the font name is not found in mapping, we assume */
+ /* that the user specified the Postscript name directly */
+
+ /* Check if this font can be found on the system */
+ string = CFStringCreateWithCString(kCFAllocatorDefault,
+ temp,
+ kCFStringEncodingMacRoman);
+ atsfont = ATSFontFindFromPostScriptName(string, kATSOptionFlagsDefault);
+ CFRelease(string);
+
+ if(atsfont)
+ {
+ name = temp;
+ break;
+ }
+ }
+ if(!atsfont)
+ { string = CFStringCreateWithCString(kCFAllocatorDefault,
+ name,
+ kCFStringEncodingMacRoman);
+ atsfont = ATSFontFindFromPostScriptName(string, kATSOptionFlagsDefault);
+ CFRelease(string);
+ }
+ CGContextSelectFont(cr, name, size, kCGEncodingMacRoman);
+ return atsfont;
+}
+
+static PyObject*
+GraphicsContext_draw_text (GraphicsContext* self, PyObject* args)
+{
+ float x;
+ float y;
+ const UniChar* text;
+ int n;
+ PyObject* family;
+ float size;
+ const char* weight;
+ const char* style;
+ float angle;
+ ATSFontRef atsfont;
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "ffu#Ofssf",
+ &x,
+ &y,
+ &text,
+ &n,
+ &family,
+ &size,
+ &weight,
+ &style,
+ &angle)) return NULL;
+
+ atsfont = setfont(cr, family, size, weight, style);
+
+ OSStatus status;
+
+ ATSUAttributeTag tags[] = {kATSUFontTag, kATSUSizeTag, kATSUQDBoldfaceTag};
+ ByteCount sizes[] = {sizeof(ATSUFontID), sizeof(Fixed), sizeof(Boolean)};
+ Fixed atsuSize = Long2Fix(size);
+ Boolean isBold = FALSE; /* setfont takes care of this */
+
+ ATSUAttributeValuePtr values[] = {&atsfont, &atsuSize, &isBold};
+ status = ATSUSetAttributes(self->style, 3, tags, sizes, values);
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUSetAttributes failed");
+ return NULL;
+ }
+
+ status = ATSUSetTextPointerLocation(self->layout,
+ text,
+ kATSUFromTextBeginning, // offset from beginning
+ kATSUToTextEnd, // length of text range
+ n); // length of text buffer
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError,
+ "ATSUCreateTextLayoutWithTextPtr failed");
+ return NULL;
+ }
+
+ status = ATSUSetRunStyle(self->layout,
+ self->style,
+ kATSUFromTextBeginning,
+ kATSUToTextEnd);
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUSetRunStyle failed");
+ return NULL;
+ }
+
+ Fixed atsuAngle = X2Fix(angle);
+ ATSUAttributeTag tags2[] = {kATSUCGContextTag, kATSULineRotationTag};
+ ByteCount sizes2[] = {sizeof (CGContextRef), sizeof(Fixed)};
+ ATSUAttributeValuePtr values2[] = {&cr, &atsuAngle};
+ status = ATSUSetLayoutControls(self->layout, 2, tags2, sizes2, values2);
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUSetLayoutControls failed");
+ return NULL;
+ }
+
+ status = ATSUDrawText(self->layout,
+ kATSUFromTextBeginning,
+ kATSUToTextEnd,
+ X2Fix(x),
+ X2Fix(y));
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUDrawText failed");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static void _data_provider_release(void* info, const void* data, size_t size)
+{
+ PyObject* image = (PyObject*)info;
+ Py_DECREF(image);
+}
+
+static PyObject*
+GraphicsContext_draw_mathtext(GraphicsContext* self, PyObject* args)
+{
+ float x, y, angle;
+ npy_intp nrows, ncols;
+ int n;
+
+ PyObject* object;
+ PyArrayObject* image;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "fffO", &x, &y, &angle, &object)) return NULL;
+
+ /* ------------- Check the image ---------------------------- */
+ if(!PyArray_Check (object))
+ {
+ PyErr_SetString(PyExc_TypeError, "image should be a NumPy array.");
+ return NULL;
+ }
+ image = (PyArrayObject*) object;
+ if(PyArray_NDIM(image) != 2)
+ {
+ PyErr_Format(PyExc_TypeError,
+ "image has incorrect rank (%d expected 2)",
+ PyArray_NDIM(image));
+ return NULL;
+ }
+ if (PyArray_TYPE(image) != NPY_UBYTE)
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "image has incorrect type (should be uint8)");
+ return NULL;
+ }
+ if (!PyArray_ISCONTIGUOUS(image))
+ {
+ PyErr_SetString(PyExc_TypeError, "image array is not contiguous");
+ return NULL;
+ }
+
+ nrows = PyArray_DIM(image, 0);
+ ncols = PyArray_DIM(image, 1);
+ if (nrows != (int) nrows || ncols != (int) ncols)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "bitmap image too large");
+ return NULL;
+ }
+ n = nrows * ncols;
+ Py_INCREF(object);
+
+ const size_t bytesPerComponent = 1;
+ const size_t bitsPerComponent = 8 * bytesPerComponent;
+ const size_t nComponents = 1; /* gray */
+ const size_t bitsPerPixel = bitsPerComponent * nComponents;
+ const size_t bytesPerRow = nComponents * bytesPerComponent * ncols;
+ CGDataProviderRef provider = CGDataProviderCreateWithData(object,
+ PyArray_DATA(image),
+ n,
+ _data_provider_release);
+ CGImageRef bitmap = CGImageMaskCreate ((int) ncols,
+ (int) nrows,
+ bitsPerComponent,
+ bitsPerPixel,
+ bytesPerRow,
+ provider,
+ NULL,
+ false);
+ CGDataProviderRelease(provider);
+
+ if(!bitmap)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGImageMaskCreate failed");
+ return NULL;
+ }
+
+ if (angle==0.0)
+ {
+ CGContextDrawImage(cr, CGRectMake(x,y,ncols,nrows), bitmap);
+ }
+ else
+ {
+ CGContextSaveGState(cr);
+ CGContextTranslateCTM(cr, x, y);
+ CGContextRotateCTM(cr, angle*M_PI/180);
+ CGContextDrawImage(cr, CGRectMake(0,0,ncols,nrows), bitmap);
+ CGContextRestoreGState(cr);
+ }
+ CGImageRelease(bitmap);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+GraphicsContext_get_text_width_height_descent(GraphicsContext* self, PyObject* args)
+{
+ const UniChar* text;
+ int n;
+ PyObject* family;
+ float size;
+ const char* weight;
+ const char* style;
+
+ ATSFontRef atsfont;
+
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "u#Ofss", &text, &n, &family, &size, &weight, &style)) return NULL;
+
+ atsfont = setfont(cr, family, size, weight, style);
+
+ OSStatus status = noErr;
+ ATSUAttributeTag tags[] = {kATSUFontTag,
+ kATSUSizeTag,
+ kATSUQDBoldfaceTag,
+ kATSUQDItalicTag};
+ ByteCount sizes[] = {sizeof(ATSUFontID),
+ sizeof(Fixed),
+ sizeof(Boolean),
+ sizeof(Boolean)};
+ Fixed atsuSize = Long2Fix(size);
+ Boolean isBold = FALSE; /* setfont takes care of this */
+ Boolean isItalic = FALSE; /* setfont takes care of this */
+ ATSUAttributeValuePtr values[] = {&atsfont, &atsuSize, &isBold, &isItalic};
+
+ status = ATSUSetAttributes(self->style, 4, tags, sizes, values);
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUSetAttributes failed");
+ return NULL;
+ }
+
+ status = ATSUSetTextPointerLocation(self->layout,
+ text,
+ kATSUFromTextBeginning, // offset from beginning
+ kATSUToTextEnd, // length of text range
+ n); // length of text buffer
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError,
+ "ATSUCreateTextLayoutWithTextPtr failed");
+ return NULL;
+ }
+
+ status = ATSUSetRunStyle(self->layout,
+ self->style,
+ kATSUFromTextBeginning,
+ kATSUToTextEnd);
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUSetRunStyle failed");
+ return NULL;
+ }
+
+ ATSUAttributeTag tag = kATSUCGContextTag;
+ ByteCount bc = sizeof (CGContextRef);
+ ATSUAttributeValuePtr value = &cr;
+ status = ATSUSetLayoutControls(self->layout, 1, &tag, &bc, &value);
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUSetLayoutControls failed");
+ return NULL;
+ }
+
+ ATSUTextMeasurement before;
+ ATSUTextMeasurement after;
+ ATSUTextMeasurement ascent;
+ ATSUTextMeasurement descent;
+ status = ATSUGetUnjustifiedBounds(self->layout,
+ kATSUFromTextBeginning, kATSUToTextEnd,
+ &before, &after, &ascent, &descent);
+ if (status!=noErr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "ATSUGetUnjustifiedBounds failed");
+ return NULL;
+ }
+
+ const float width = FixedToFloat(after-before);
+ const float height = FixedToFloat(ascent-descent);
+
+ return Py_BuildValue("fff", width, height, FixedToFloat(descent));
+}
+
+static PyObject*
+GraphicsContext_draw_image(GraphicsContext* self, PyObject* args)
+{
+ float x, y;
+ int nrows, ncols;
+ const char* data;
+ int n;
+ PyObject* image;
+ CGContextRef cr = self->cr;
+ if (!cr)
+ {
+ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "ffiiO", &x,
+ &y,
+ &nrows,
+ &ncols,
+ &image)) return NULL;
+
+ if (!PyString_Check(image))
+ {
+ PyErr_SetString(PyExc_RuntimeError, "image is not a string");
+ return NULL;
+ }
+
+ const size_t bytesPerComponent = 1;
+ const size_t bitsPerComponent = 8 * bytesPerComponent;
+ const size_t nComponents = 4; /* red, green, blue, alpha */
+ const size_t bitsPerPixel = bitsPerComponent * nComponents;
+ const size_t bytesPerRow = nComponents * bytesPerComponent * ncols;
+ CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
+
+ Py_INCREF(image);
+ n = PyString_GET_SIZE(image);
+ data = PyString_AsString(image);
+
+ CGDataProviderRef provider = CGDataProviderCreateWithData(image,
+ data,
+ n,
+ _data_provider_release);
+ CGImageRef bitmap = CGImageCreate (ncols,
+ nrows,
+ bitsPerComponent,
+ bitsPerPixel,
+ bytesPerRow,
+ colorspace,
+ kCGImageAlphaLast,
+ provider,
+ ...
[truncated message content] |