2
# :Copyright: 2004, 2007, Enthought, Inc.
7
include "CoreFoundation.pxi"
8
include "CoreGraphics.pxi"
9
include "QuickDraw.pxi"
18
from ATSFont import default_font_info
20
cdef extern from "math.h":
21
double sqrt(double arg)
24
cdef CFURLRef url_from_filename(char* filename) except NULL:
25
cdef CFStringRef filePath
26
filePath = CFStringCreateWithCString(NULL, filename,
27
kCFStringEncodingUTF8)
29
raise RuntimeError("could not create CFStringRef")
32
cfurl = CFURLCreateWithFileSystemPath(NULL, filePath,
33
kCFURLPOSIXPathStyle, 0)
36
raise RuntimeError("could not create a CFURLRef")
43
round = kCGLineCapRound
44
square = kCGLineCapSquare
47
miter = kCGLineJoinMiter
48
round = kCGLineJoinRound
49
bevel = kCGLineJoinBevel
51
class PathDrawingMode:
53
eof_fill = kCGPathEOFill
54
stroke = kCGPathStroke
55
fill_stroke = kCGPathFillStroke
56
eof_fill_stroke = kCGPathEOFillStroke
59
min_x_edge = CGRectMinXEdge
60
min_y_edge = CGRectMinYEdge
61
max_x_edge = CGRectMaxXEdge
62
max_y_edge = CGRectMaxYEdge
64
class ColorRenderingIntent:
65
default = kCGRenderingIntentDefault
66
absolute_colorimetric = kCGRenderingIntentAbsoluteColorimetric
67
realative_colorimetric = kCGRenderingIntentRelativeColorimetric
68
perceptual = kCGRenderingIntentPerceptual
69
saturation = kCGRenderingIntentSaturation
72
# gray = kCGColorSpaceUserGray
73
# rgb = kCGColorSpaceUserRGB
74
# cmyk = kCGColorSpaceUserCMYK
77
index_max = kCGFontIndexMax
78
index_invalid = kCGFontIndexInvalid
79
glyph_max = kCGGlyphMax
81
class TextDrawingMode:
83
stroke = kCGTextStroke
84
fill_stroke = kCGTextFillStroke
85
invisible = kCGTextInvisible
86
fill_clip = kCGTextFillClip
87
stroke_clip = kCGTextStrokeClip
88
fill_stroke_clip = kCGTextFillStrokeClip
92
font_specific = kCGEncodingFontSpecific
93
mac_roman = kCGEncodingMacRoman
96
none = kCGImageAlphaNone
97
premultiplied_last = kCGImageAlphaPremultipliedLast
98
premultiplied_first = kCGImageAlphaPremultipliedFirst
99
last = kCGImageAlphaLast
100
first = kCGImageAlphaFirst
101
none_skip_last = kCGImageAlphaNoneSkipLast
102
none_skip_first = kCGImageAlphaNoneSkipFirst
103
only = kCGImageAlphaOnly
105
class InterpolationQuality:
106
default = kCGInterpolationDefault
107
none = kCGInterpolationNone
108
low = kCGInterpolationLow
109
high = kCGInterpolationHigh
111
class PathElementType:
112
move_to = kCGPathElementMoveToPoint,
113
line_to = kCGPathElementAddLineToPoint,
114
quad_curve_to = kCGPathElementAddQuadCurveToPoint,
115
curve_to = kCGPathElementAddCurveToPoint,
116
close_path = kCGPathElementCloseSubpath
118
class StringEncoding:
119
mac_roman = kCFStringEncodingMacRoman
120
windows_latin1 = kCFStringEncodingWindowsLatin1
121
iso_latin1 = kCFStringEncodingISOLatin1
122
nextstep_latin = kCFStringEncodingNextStepLatin
123
ascii = kCFStringEncodingASCII
124
unicode = kCFStringEncodingUnicode
125
utf8 = kCFStringEncodingUTF8
126
nonlossy_ascii = kCFStringEncodingNonLossyASCII
129
posix = kCFURLPOSIXPathStyle
130
hfs = kCFURLHFSPathStyle
131
windows = kCFURLWindowsPathStyle
133
c_numpy.import_array()
136
from enthought.kiva import constants
139
cap_style[constants.CAP_ROUND] = kCGLineCapRound
140
cap_style[constants.CAP_SQUARE] = kCGLineCapSquare
141
cap_style[constants.CAP_BUTT] = kCGLineCapButt
144
join_style[constants.JOIN_ROUND] = kCGLineJoinRound
145
join_style[constants.JOIN_BEVEL] = kCGLineJoinBevel
146
join_style[constants.JOIN_MITER] = kCGLineJoinMiter
149
draw_modes[constants.FILL] = kCGPathFill
150
draw_modes[constants.EOF_FILL] = kCGPathEOFill
151
draw_modes[constants.STROKE] = kCGPathStroke
152
draw_modes[constants.FILL_STROKE] = kCGPathFillStroke
153
draw_modes[constants.EOF_FILL_STROKE] = kCGPathEOFillStroke
156
text_modes[constants.TEXT_FILL] = kCGTextFill
157
text_modes[constants.TEXT_STROKE] = kCGTextStroke
158
text_modes[constants.TEXT_FILL_STROKE] = kCGTextFillStroke
159
text_modes[constants.TEXT_INVISIBLE] = kCGTextInvisible
160
text_modes[constants.TEXT_FILL_CLIP] = kCGTextFillClip
161
text_modes[constants.TEXT_STROKE_CLIP] = kCGTextStrokeClip
162
text_modes[constants.TEXT_FILL_STROKE_CLIP] = kCGTextFillStrokeClip
163
text_modes[constants.TEXT_CLIP] = kCGTextClip
164
# this last one doesn't exist in Quartz
165
text_modes[constants.TEXT_OUTLINE] = kCGTextStroke
168
cdef class CGContextInABox(CGContext)
170
cdef class CGPDFDocument
172
cdef class CGLayerContext(CGContextInABox)
173
cdef class CGGLContext(CGContextInABox)
174
cdef class CGBitmapContext(CGContext)
175
cdef class CGPDFContext(CGContext)
176
cdef class CGImageMask(CGImage)
178
cdef class CGMutablePath
181
cdef class CGContext:
182
cdef CGContextRef context
183
cdef long can_release
184
cdef object current_font
185
cdef ATSUStyle current_style
186
cdef CGAffineTransform text_matrix
187
cdef object style_cache
189
def __new__(self, *args, **kwds):
191
self.current_style = NULL
193
self.text_matrix = CGAffineTransformMake(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
195
def __init__(self, long context, long can_release=0):
196
self.context = <CGContextRef>context
198
self.can_release = can_release
200
self._setup_color_space()
203
def _setup_color_space(self):
204
# setup an RGB color space
205
cdef CGColorSpaceRef space
207
space = CGColorSpaceCreateDeviceRGB()
208
CGContextSetFillColorSpace(self.context, space)
209
CGContextSetStrokeColorSpace(self.context, space)
210
CGColorSpaceRelease(space)
212
def _setup_fonts(self):
213
self.style_cache = {}
214
self.select_font("Helvetica", 12)
215
CGContextSetShouldSmoothFonts(self.context, 1)
216
CGContextSetShouldAntialias(self.context, 1)
218
#----------------------------------------------------------------
219
# Coordinate Transform Matrix Manipulation
220
#----------------------------------------------------------------
222
def scale_ctm(self, float sx, float sy):
223
""" Set the coordinate system scale to the given values, (sx,sy).
225
sx:float -- The new scale factor for the x axis
226
sy:float -- The new scale factor for the y axis
228
CGContextScaleCTM(self.context, sx, sy)
230
def translate_ctm(self, float tx, float ty):
231
""" Translate the coordinate system by the given value by (tx,ty)
233
tx:float -- The distance to move in the x direction
234
ty:float -- The distance to move in the y direction
236
CGContextTranslateCTM(self.context, tx, ty)
238
def rotate_ctm(self, float angle):
239
""" Rotates the coordinate space for drawing by the given angle.
241
angle:float -- the angle, in radians, to rotate the coordinate
244
CGContextRotateCTM(self.context, angle)
246
def concat_ctm(self, object transform):
247
""" Concatenate the transform to current coordinate transform matrix.
249
transform:affine_matrix -- the transform matrix to concatenate with
250
the current coordinate matrix.
252
cdef float a,b,c,d,tx,ty
253
a,b,c,d,tx,ty = transform
255
cdef CGAffineTransform atransform
256
atransform = CGAffineTransformMake(a,b,c,d,tx,ty)
258
CGContextConcatCTM(self.context, atransform)
261
""" Return the current coordinate transform matrix.
263
cdef CGAffineTransform t
264
t = CGContextGetCTM(self.context)
265
return (t.a, t.b, t.c, t.d, t.tx, t.ty)
267
def get_ctm_scale(self):
268
""" Returns the average scaling factor of the transform matrix.
270
This isn't really part of the GC interface, but it is a convenience
271
method to make up for us not having full AffineMatrix support in the
274
cdef CGAffineTransform t
275
t = CGContextGetCTM(self.context)
276
x = sqrt(2.0) / 2.0 * (t.a + t.b)
277
y = sqrt(2.0) / 2.0 * (t.c + t.d)
278
return sqrt(x*x + y*y)
282
#----------------------------------------------------------------
283
# Save/Restore graphics state.
284
#----------------------------------------------------------------
286
def save_state(self):
287
""" Save the current graphic's context state.
289
This should always be paired with a restore_state
291
CGContextSaveGState(self.context)
293
def restore_state(self):
294
""" Restore the previous graphics state.
296
CGContextRestoreGState(self.context)
298
#----------------------------------------------------------------
299
# Manipulate graphics state attributes.
300
#----------------------------------------------------------------
302
def set_antialias(self, bool value):
303
""" Set/Unset antialiasing for bitmap graphics context.
305
CGContextSetShouldAntialias(self.context, value)
307
def set_line_width(self, float width):
308
""" Set the line width for drawing
310
width:float -- The new width for lines in user space units.
312
CGContextSetLineWidth(self.context, width)
314
def set_line_join(self, object style):
315
""" Set style for joining lines in a drawing.
317
style:join_style -- The line joining style. The available
318
styles are JOIN_ROUND, JOIN_BEVEL, JOIN_MITER.
321
sjoin = join_style[style]
323
msg = "Invalid line join style. See documentation for valid styles"
324
raise ValueError(msg)
325
CGContextSetLineJoin(self.context, sjoin)
327
def set_miter_limit(self, float limit):
328
""" Specifies limits on line lengths for mitering line joins.
330
If line_join is set to miter joins, the limit specifies which
331
line joins should actually be mitered. If lines aren't mitered,
332
they are joined with a bevel. The line width is divided by
333
the length of the miter. If the result is greater than the
334
limit, the bevel style is used.
336
limit:float -- limit for mitering joins.
338
CGContextSetMiterLimit(self.context, limit)
340
def set_line_cap(self, object style):
341
""" Specify the style of endings to put on line ends.
343
style:cap_style -- the line cap style to use. Available styles
344
are CAP_ROUND,CAP_BUTT,CAP_SQUARE
347
scap = cap_style[style]
349
msg = "Invalid line cap style. See documentation for valid styles"
350
raise ValueError(msg)
351
CGContextSetLineCap(self.context, scap)
353
def set_line_dash(self, object lengths, float phase=0.0):
355
lengths:float array -- An array of floating point values
356
specifing the lengths of on/off painting
358
phase:float -- Specifies how many units into dash pattern
359
to start. phase defaults to 0.
366
# No dash; solid line.
367
CGContextSetLineDash(self.context, 0.0, NULL, 0)
371
flengths = <float*>PyMem_Malloc(n*sizeof(float))
373
raise MemoryError("could not allocate %s floats" % n)
374
for i from 0 <= i < n:
375
flengths[i] = lengths[i]
376
CGContextSetLineDash(self.context, phase, flengths, n)
379
def set_flatness(self, float flatness):
381
It is device dependent and therefore not recommended by
382
the PDF documentation.
384
CGContextSetFlatness(self.context, flatness)
386
#----------------------------------------------------------------
387
# Sending drawing data to a device
388
#----------------------------------------------------------------
391
""" Send all drawing data to the destination device.
393
CGContextFlush(self.context)
395
def synchronize(self):
396
""" Prepares drawing data to be updated on a destination device.
398
CGContextSynchronize(self.context)
400
#----------------------------------------------------------------
402
#----------------------------------------------------------------
404
def begin_page(self, media_box=None):
405
""" Create a new page within the graphics context.
408
cdef CGRect* mbox_ptr
409
if media_box is not None:
410
mbox = CGRectMakeFromPython(media_box)
415
CGContextBeginPage(self.context, mbox_ptr)
418
""" End drawing in the current page of the graphics context.
420
CGContextEndPage(self.context)
422
#----------------------------------------------------------------
423
# Building paths (contours that are drawn)
425
# + Currently, nothing is drawn as the path is built. Instead, the
426
# instructions are stored and later drawn. Should this be changed?
427
# We will likely draw to a buffer instead of directly to the canvas
430
# Hmmm. No. We have to keep the path around for storing as a
431
# clipping region and things like that.
433
# + I think we should keep the current_path_point hanging around.
435
#----------------------------------------------------------------
437
def begin_path(self):
438
""" Clear the current drawing path and begin a new one.
440
CGContextBeginPath(self.context)
442
def move_to(self, float x, float y):
443
""" Start a new drawing subpath at place the current point at (x,y).
445
CGContextMoveToPoint(self.context, x,y)
447
def line_to(self, float x, float y):
448
""" Add a line from the current point to the given point (x,y).
450
The current point is moved to (x,y).
452
CGContextAddLineToPoint(self.context, x,y)
454
def lines(self, object points):
455
""" Add a series of lines as a new subpath.
457
Points is an Nx2 array of x,y pairs.
459
current_point is moved to the last point in points
464
cdef c_numpy.ndarray apoints
469
# Shortcut for the 0 and 1 point case
473
apoints = <c_numpy.ndarray>(numpy.asarray(points, dtype=numpy.float32))
475
if apoints.nd != 2 or apoints.dimensions[1] != 2:
476
msg = "must pass array of 2-D points"
477
raise ValueError(msg)
479
x = (<float*>c_numpy.PyArray_GETPTR2(apoints, 0, 0))[0]
480
y = (<float*>c_numpy.PyArray_GETPTR2(apoints, 0, 1))[0]
481
CGContextMoveToPoint(self.context, x, y)
482
for i from 1 <= i < n:
483
x = (<float*>c_numpy.PyArray_GETPTR2(apoints, i, 0))[0]
484
y = (<float*>c_numpy.PyArray_GETPTR2(apoints, i, 1))[0]
485
CGContextAddLineToPoint(self.context, x, y)
487
def line_set(self, object starts, object ends):
488
""" Adds a series of disconnected line segments as a new subpath.
490
starts and ends are Nx2 arrays of (x,y) pairs indicating the
491
starting and ending points of each line segment.
493
current_point is moved to the last point in ends
501
for i from 0 <= i < n:
502
CGContextMoveToPoint(self.context, starts[i][0], starts[i][1])
503
CGContextAddLineToPoint(self.context, ends[i][0], ends[i][1])
505
def rect(self, float x, float y, float sx, float sy):
506
""" Add a rectangle as a new subpath.
508
CGContextAddRect(self.context, CGRectMake(x,y,sx,sy))
510
def rects(self, object rects):
511
""" Add multiple rectangles as separate subpaths to the path.
516
for i from 0 <= i < n:
517
CGContextAddRect(self.context, CGRectMakeFromPython(rects[i]))
519
def close_path(self):
520
""" Close the path of the current subpath.
522
CGContextClosePath(self.context)
524
def curve_to(self, float cp1x, float cp1y, float cp2x, float cp2y,
528
CGContextAddCurveToPoint(self.context, cp1x, cp1y, cp2x, cp2y, x, y )
530
def quad_curve_to(self, float cpx, float cpy, float x, float y):
533
CGContextAddQuadCurveToPoint(self.context, cpx, cpy, x, y)
535
def arc(self, float x, float y, float radius, float start_angle,
536
float end_angle, bool clockwise=False):
539
CGContextAddArc(self.context, x, y, radius, start_angle, end_angle,
542
def arc_to(self, float x1, float y1, float x2, float y2, float radius):
545
CGContextAddArcToPoint(self.context, x1, y1, x2, y2, radius)
547
def add_path(self, CGMutablePath path not None):
550
CGContextAddPath(self.context, path.path)
552
#----------------------------------------------------------------
553
# Getting information on paths
554
#----------------------------------------------------------------
556
def is_path_empty(self):
557
""" Test to see if the current drawing path is empty
559
return CGContextIsPathEmpty(self.context)
561
def get_path_current_point(self):
562
""" Return the current point from the graphics context.
564
Note: This should be a tuple or array.
568
result = CGContextGetPathCurrentPoint(self.context)
569
return result.x, result.y
571
def get_path_bounding_box(self):
573
should return a tuple or array instead of a strange object.
576
result = CGContextGetPathBoundingBox(self.context)
577
return (result.origin.x, result.origin.y,
578
result.size.width, result.size.height)
580
#----------------------------------------------------------------
581
# Clipping path manipulation
582
#----------------------------------------------------------------
587
CGContextClip(self.context)
589
def even_odd_clip(self):
592
CGContextEOClip(self.context)
594
def clip_to_rect(self, float x, float y, float width, float height):
595
""" Clip context to the given rectangular region.
597
CGContextClipToRect(self.context, CGRectMake(x,y,width,height))
599
def clip_to_rects(self, object rects):
607
cgrects = <CGRect*>PyMem_Malloc(n*sizeof(CGRect))
609
raise MemoryError("could not allocate memory for CGRects")
611
for i from 0 <= i < n:
612
cgrects[i] = CGRectMakeFromPython(rects[i])
613
CGContextClipToRects(self.context, cgrects, n)
617
#----------------------------------------------------------------
618
# Color space manipulation
620
# I'm not sure we'll mess with these at all. They seem to
621
# be for setting the color system. Hard coding to RGB or
622
# RGBA for now sounds like a reasonable solution.
623
#----------------------------------------------------------------
625
def set_fill_color_space(self):
628
msg = "set_fill_color_space not implemented on Macintosh yet."
629
raise NotImplementedError(msg)
631
def set_stroke_color_space(self):
634
msg = "set_stroke_color_space not implemented on Macintosh yet."
635
raise NotImplementedError(msg)
637
def set_rendering_intent(self, intent):
640
CGContextSetRenderingIntent(self.context, intent)
642
#----------------------------------------------------------------
644
#----------------------------------------------------------------
646
def set_fill_color(self, object color):
654
CGContextSetRGBFillColor(self.context, r, g, b, a)
656
def set_stroke_color(self, object color):
664
CGContextSetRGBStrokeColor(self.context, r, g, b, a)
666
def set_alpha(self, float alpha):
669
CGContextSetAlpha(self.context, alpha)
671
#def set_gray_fill_color(self):
676
#def set_gray_stroke_color(self):
681
#def set_rgb_fill_color(self):
686
#def set_rgb_stroke_color(self):
691
#def cmyk_fill_color(self):
696
#def cmyk_stroke_color(self):
701
#----------------------------------------------------------------
703
#----------------------------------------------------------------
705
def draw_image(self, object image, object rect=None):
706
""" Draw an image or another CGContext onto a region.
709
rect = (0, 0, self.width(), self.height())
710
if isinstance(image, numpy.ndarray):
711
self._draw_cgimage(CGImage(image), rect)
712
elif isinstance(image, CGImage):
713
self._draw_cgimage(image, rect)
714
elif hasattr(image, 'bmp_array'):
715
self._draw_cgimage(CGImage(image.bmp_array), rect)
716
elif isinstance(image, CGLayerContext):
717
self._draw_cglayer(image, rect)
719
raise TypeError("could not recognize image %r" % type(image))
721
def _draw_cgimage(self, CGImage image, object rect):
722
""" Draw a CGImage into a region.
724
CGContextDrawImage(self.context, CGRectMakeFromPython(rect),
727
def _draw_cglayer(self, CGLayerContext layer, object rect):
728
""" Draw a CGLayer into a region.
730
CGContextDrawLayerInRect(self.context, CGRectMakeFromPython(rect),
733
def set_interpolation_quality(self, quality):
734
CGContextSetInterpolationQuality(self.context, quality)
736
#----------------------------------------------------------------
737
# Drawing PDF documents
738
#----------------------------------------------------------------
740
def draw_pdf_document(self, object rect, CGPDFDocument document not None,
743
rect:(x,y,width,height) -- rectangle to draw into
744
document:CGPDFDocument -- PDF file to read from
745
page=1:int -- page number of PDF file
748
cgrect = CGRectMakeFromPython(rect)
750
CGContextDrawPDFDocument(self.context, cgrect, document.document, page)
753
#----------------------------------------------------------------
755
#----------------------------------------------------------------
757
def select_font(self, object name, float size, style='regular'):
760
cdef ATSUStyle atsu_style
762
key = (name, size, style)
763
if key not in self.style_cache:
764
font = default_font_info.lookup(name, style=style)
765
self.current_font = font
766
ps_name = font.postscript_name
768
atsu_style = _create_atsu_style(ps_name, size)
769
if atsu_style == NULL:
770
raise RuntimeError("could not create style for font %r" % ps_name)
771
self.style_cache[key] = PyCObject_FromVoidPtr(<void*>atsu_style,
772
<cobject_destr>ATSUDisposeStyle)
774
atsu_style = <ATSUStyle>PyCObject_AsVoidPtr(self.style_cache[key])
775
self.current_style = atsu_style
777
def set_font(self, font):
778
""" Set the font for the current graphics context.
780
I need to figure out this one.
784
constants.NORMAL: 'regular',
785
constants.BOLD: 'bold',
786
constants.ITALIC: 'italic',
787
constants.BOLD_ITALIC: 'bold italic',
788
}[font.weight | font.style]
789
self.select_font(font.face_name, font.size, style=style)
791
def set_font_size(self, float size):
794
cdef ATSUAttributeTag attr_tag
795
cdef ByteCount attr_size
796
cdef ATSUAttributeValuePtr attr_value
797
cdef Fixed fixed_size
800
if self.current_style == NULL:
803
attr_tag = kATSUSizeTag
804
attr_size = sizeof(Fixed)
805
fixed_size = FloatToFixed(size)
806
attr_value = <ATSUAttributeValuePtr>&fixed_size
807
err = ATSUSetAttributes(self.current_style, 1, &attr_tag, &attr_size, &attr_value)
809
raise RuntimeError("could not set font size on current style")
811
def set_character_spacing(self, float spacing):
815
# XXX: This does not fit in with ATSUI, really.
816
CGContextSetCharacterSpacing(self.context, spacing)
818
def set_text_drawing_mode(self, object mode):
822
cgmode = text_mode[mode]
824
msg = "Invalid text drawing mode. See documentation for valid modes"
825
raise ValueError(msg)
826
CGContextSetTextDrawingMode(self.context, cgmode)
828
def set_text_position(self, float x,float y):
831
self.text_matrix.tx = x
832
self.text_matrix.ty = y
834
def get_text_position(self):
837
return self.text_matrix.tx, self.text_matrix.ty
839
def set_text_matrix(self, object ttm):
842
cdef float a,b,c,d,tx,ty
847
cdef CGAffineTransform transform
848
transform = CGAffineTransformMake(a,b,c,d,tx,ty)
849
self.text_matrix = transform
851
def get_text_matrix(self):
854
return ((self.text_matrix.a, self.text_matrix.b, 0.0),
855
(self.text_matrix.c, self.text_matrix.d, 0.0),
856
(self.text_matrix.tx,self.text_matrix.ty,1.0))
858
def get_text_extent(self, object text):
859
""" Measure the space taken up by given text using the current font.
863
cdef ATSFontMetrics metrics
865
cdef ATSUTextLayout layout
866
cdef double x1, x2, y1, y2
867
cdef ATSUTextMeasurement before, after, ascent, descent
868
cdef ByteCount actual_size
873
# ATSUGetUnjustifiedBounds does not handle empty strings.
878
_create_atsu_layout(text, self.current_style, &layout)
879
status = ATSUGetUnjustifiedBounds(layout, 0, len(text), &before,
880
&after, &ascent, &descent)
882
raise RuntimeError("could not calculate font metrics")
888
x1 = FixedToFloat(before)
889
x2 = FixedToFloat(after)
891
y1 = -FixedToFloat(descent)
892
y2 = -y1 + FixedToFloat(ascent)
896
ATSUDisposeTextLayout(layout)
898
return x1, y1, x2, y2
900
def get_full_text_extent(self, object text):
901
""" Backwards compatibility API over .get_text_extent() for Enable.
904
x1, y1, x2, y2 = self.get_text_extent(text)
906
return x2, y2, y1, x1
909
def show_text(self, object text, object xy=None):
910
""" Draw text on the device at current text position.
912
This is also used for showing text at a particular point
913
specified by xy == (x, y).
917
cdef CGAffineTransform text_matrix
918
cdef ATSUTextLayout layout
921
# I don't think we can draw empty strings using the ATSU API.
933
CGContextConcatCTM(self.context, self.text_matrix)
934
_create_atsu_layout(text, self.current_style, &layout)
935
_set_cgcontext_for_layout(self.context, layout)
936
ATSUDrawText(layout, 0, len(text), FloatToFixed(x), FloatToFixed(y))
940
ATSUDisposeTextLayout(layout)
942
def show_text_at_point(self, object text, float x, float y):
943
""" Draw text on the device at a given text position.
945
self.show_text(text, (x, y))
947
def show_glyphs(self):
950
msg = "show_glyphs not implemented on Macintosh yet."
951
raise NotImplementedError(msg)
953
#----------------------------------------------------------------
954
# Painting paths (drawing and filling contours)
955
#----------------------------------------------------------------
957
def stroke_path(self):
960
CGContextStrokePath(self.context)
965
CGContextFillPath(self.context)
967
def eof_fill_path(self):
970
CGContextEOFillPath(self.context)
972
def stroke_rect(self, object rect):
975
CGContextStrokeRect(self.context, CGRectMakeFromPython(rect))
977
def stroke_rect_with_width(self, object rect, float width):
980
CGContextStrokeRectWithWidth(self.context, CGRectMakeFromPython(rect), width)
982
def fill_rect(self, object rect):
985
CGContextFillRect(self.context, CGRectMakeFromPython(rect))
987
def fill_rects(self, object rects):
995
cgrects = <CGRect*>PyMem_Malloc(n*sizeof(CGRect))
997
raise MemoryError("could not allocate memory for CGRects")
999
for i from 0 <= i < n:
1000
cgrects[i] = CGRectMakeFromPython(rects[i])
1002
CGContextFillRects(self.context, cgrects, n)
1004
def clear_rect(self, object rect):
1007
CGContextClearRect(self.context, CGRectMakeFromPython(rect))
1009
def draw_path(self, object mode=constants.FILL_STROKE):
1010
""" Walk through all the drawing subpaths and draw each element.
1012
Each subpath is drawn separately.
1015
cg_mode = draw_modes[mode]
1016
CGContextDrawPath(self.context, cg_mode)
1018
def draw_rect(self, rect, object mode=constants.FILL_STROKE):
1019
""" Draw a rectangle with the given mode.
1023
CGContextBeginPath(self.context)
1024
CGContextAddRect(self.context, CGRectMakeFromPython(rect))
1025
cg_mode = draw_modes[mode]
1026
CGContextDrawPath(self.context, cg_mode)
1027
self.restore_state()
1029
def get_empty_path(self):
1030
""" Return a path object that can be built up and then reused.
1033
return CGMutablePath()
1035
def draw_path_at_points(self, points, CGMutablePath marker not None,
1036
object mode=constants.FILL_STROKE):
1040
cdef c_numpy.ndarray apoints
1043
apoints = <c_numpy.ndarray>(numpy.asarray(points, dtype=numpy.float32))
1045
if apoints.nd != 2 or apoints.dimensions[1] != 2:
1046
msg = "must pass array of 2-D points"
1047
raise ValueError(msg)
1049
cg_mode = draw_modes[mode]
1052
for i from 0 <= i < n:
1053
x = (<float*>c_numpy.PyArray_GETPTR2(apoints, i, 0))[0]
1054
y = (<float*>c_numpy.PyArray_GETPTR2(apoints, i, 1))[0]
1055
CGContextSaveGState(self.context)
1056
CGContextTranslateCTM(self.context, x, y)
1057
CGContextAddPath(self.context, marker.path)
1058
CGContextDrawPath(self.context, cg_mode)
1059
CGContextRestoreGState(self.context)
1061
def linear_gradient(self, x1, y1, x2, y2, stops, spread_method, units='userSpaceOnUse'):
1063
stops_list = stops.transpose().tolist()
1064
func = PiecewiseLinearColorFunction(stops_list)
1065
shading = AxialShading(func, (x1,y1), (x2,y2),
1066
extend_start=1, extend_end=1)
1067
self.draw_shading(shading)
1069
def radial_gradient(self, cx, cy, r, fx, fy, stops, spread_method, units='userSpaceOnUse'):
1071
stops_list = stops.transpose().tolist()
1072
func = PiecewiseLinearColorFunction(stops_list)
1073
shading = RadialShading(func, (fx, fy), 0.0, (cx, cy), r,
1074
extend_start=1, extend_end=1)
1075
self.draw_shading(shading)
1077
def draw_shading(self, Shading shading not None):
1078
CGContextDrawShading(self.context, shading.shading)
1081
#----------------------------------------------------------------
1082
# Extra routines that aren't part of DisplayPDF
1084
# Some access to font metrics are needed for laying out text.
1085
# Not sure how to handle this yet. The candidates below are
1086
# from Piddle. Perhaps there is another alternative?
1088
#----------------------------------------------------------------
1090
#def font_height(self):
1091
# '''Find the total height (ascent + descent) of the given font.'''
1092
# #return self.font_ascent() + self.font_descent()
1094
#def font_ascent(self):
1095
# '''Find the ascent (height above base) of the given font.'''
1098
#def font_descent(self):
1099
# '''Find the descent (extent below base) of the given font.'''
1100
# extents = self.dc.GetFullTextExtent(' ', wx_font)
1103
def __dealloc__(self):
1104
if self.context != NULL and self.can_release:
1105
CGContextRelease(self.context)
1108
# The following are Quartz APIs not in Kiva
1110
def set_pattern_phase(self, float tx, float ty):
1112
tx,ty:floats -- A translation in user-space to apply to a
1113
pattern before it is drawn
1115
CGContextSetPatternPhase(self.context, CGSizeMake(tx, ty))
1117
def set_should_smooth_fonts(self, bool value):
1119
value:bool -- specify whether to enable font smoothing or not
1121
CGContextSetShouldSmoothFonts(self.context, value)
1123
cdef class CGContextInABox(CGContext):
1124
""" A CGContext that knows its size.
1126
cdef readonly object size
1127
cdef readonly int _width
1128
cdef readonly int _height
1130
def __init__(self, long context, object size, long can_release=0):
1131
self.context = <CGContextRef>context
1133
self.can_release = can_release
1135
self._width, self._height = size
1137
self._setup_color_space()
1140
def clear(self, object clear_color=(1.0,1.0,1.0,1.0)):
1142
# Reset the transformation matrix back to the identity.
1143
CGContextConcatCTM(self.context,
1144
CGAffineTransformInvert(CGContextGetCTM(self.context)))
1145
self.set_fill_color(clear_color)
1146
CGContextFillRect(self.context, CGRectMake(0,0,self._width,self._height))
1147
self.restore_state()
1156
cdef class CGLayerContext(CGContextInABox):
1157
cdef CGLayerRef layer
1160
def __init__(self, CGContext gc not None, object size):
1161
self.gc = <object>gc
1162
self.layer = CGLayerCreateWithContext(gc.context,
1163
CGSizeMake(size[0], size[1]), NULL)
1164
self.context = CGLayerGetContext(self.layer)
1166
self._width, self._height = size
1167
self.can_release = 1
1169
self._setup_color_space()
1172
def __dealloc__(self):
1173
if self.layer != NULL:
1174
CGLayerRelease(self.layer)
1176
# The documentation doesn't say whether I need to release the
1177
# context derived from the layer or not. I believe that means
1182
def save(self, object filename, file_format=None, pil_options=None):
1183
""" Save the GraphicsContext to a file. Output files are always saved
1184
in RGB or RGBA format; if this GC is not in one of these formats, it is
1185
automatically converted.
1187
If filename includes an extension, the image format is inferred from it.
1188
file_format is only required if the format can't be inferred from the
1189
filename (e.g. if you wanted to save a PNG file as a .dat or .bin).
1191
filename may also be "file-like" object such as a StringIO, in which
1192
case a file_format must be supplied.
1194
pil_options is a dict of format-specific options that are passed down to
1195
the PIL image file writer. If a writer doesn't recognize an option, it
1196
is silently ignored.
1198
If the image has an alpha channel and the specified output file format
1199
does not support alpha, the image is saved in rgb24 format.
1202
cdef CGBitmapContext bmp
1204
# Create a CGBitmapContext from this layer, draw to it, then let it save
1206
rect = (0, 0) + self.size
1207
bmp = CGBitmapContext(self.size)
1208
CGContextDrawLayerInRect(bmp.context, CGRectMakeFromPython(rect), self.layer)
1209
bmp.save(filename, file_format=file_format, pil_options=pil_options)
1212
cdef class CGContextFromSWIG(CGContext):
1213
def __init__(self, swig_obj):
1214
self.can_release = False
1215
ptr = int(swig_obj.this.split('_')[1], 16)
1216
CGContext.__init__(self, ptr)
1218
cdef class CGContextForPort(CGContextInABox):
1219
cdef readonly long port
1220
cdef readonly int _begun
1222
def __init__(self, long port):
1223
cdef OSStatus status
1225
# status = QDBeginCGContext(<CGrafPtr>port, &(self.context))
1228
# raise RuntimeError("QuickDraw could not make CGContext")
1231
self.can_release = 0
1237
GetPortBounds(<CGrafPtr>port, &r)
1239
self._width = r.right - r.left
1240
self._height = r.bottom - r.top
1241
self.size = (self._width, self._height)
1246
cdef OSStatus status
1247
cdef QDRect port_rect
1249
status = QDBeginCGContext(<CGrafPtr>self.port, &(self.context))
1251
raise RuntimeError("QuickDraw could not make CGContext")
1252
SyncCGContextOriginWithPort(self.context, <CGrafPtr>self.port)
1253
self._setup_color_space()
1256
#GetPortBounds(<CGrafPtr>self.port, &port_rect)
1257
#CGContextTranslateCTM(self.context, 0, (port_rect.bottom - port_rect.top))
1258
#CGContextScaleCTM(self.context, 1.0, -1.0)
1262
if self.port and self.context and self._begun:
1263
CGContextFlush(self.context)
1264
QDEndCGContext(<CGrafPtr>(self.port), &(self.context))
1267
def clear(self, object clear_color=(1.0,1.0,1.0,1.0)):
1268
already_begun = self._begun
1270
CGContextInABox.clear(self, clear_color)
1271
if not already_begun:
1274
def __dealloc__(self):
1276
#if self.port and self.context and self._begun:
1277
# QDEndCGContext(<CGrafPtr>(self.port), &(self.context))
1278
#if self.context and self.can_release:
1279
# CGContextRelease(self.context)
1280
# self.context = NULL
1284
cdef class CGGLContext(CGContextInABox):
1285
cdef readonly long glcontext
1287
def __init__(self, long glcontext, int width, int height):
1289
raise ValueError("Need a valid pointer")
1291
self.glcontext = glcontext
1293
self.context = CGGLContextCreate(<void*>glcontext,
1294
CGSizeMake(width, height), NULL)
1295
if self.context == NULL:
1296
raise RuntimeError("could not create CGGLContext")
1297
self.can_release = 1
1300
self._height = height
1301
self.size = (self._width, self._height)
1303
self._setup_color_space()
1307
def resize(self, int width, int height):
1308
CGGLContextUpdateViewportSize(self.context, CGSizeMake(width, height))
1310
self._height = height
1311
self.size = (width, height)
1315
cdef class CGPDFContext(CGContext):
1316
cdef readonly char* filename
1317
cdef CGRect media_box
1318
def __init__(self, char* filename, rect=None):
1320
cfurl = url_from_filename(filename)
1322
cdef CGRect* cgrect_ptr
1325
cgrect = CGRectMake(0,0,612,792)
1326
cgrect_ptr = &cgrect
1328
cgrect = CGRectMakeFromPython(rect)
1329
cgrect_ptr = &cgrect
1330
self.context = CGPDFContextCreateWithURL(cfurl, cgrect_ptr, NULL)
1333
self.filename = filename
1334
self.media_box = cgrect
1336
if self.context == NULL:
1337
raise RuntimeError("could not create CGPDFContext")
1338
self.can_release = 1
1340
self._setup_color_space()
1343
CGContextBeginPage(self.context, cgrect_ptr)
1345
def begin_page(self, media_box=None):
1346
cdef CGRect* box_ptr
1348
if media_box is None:
1349
box_ptr = &(self.media_box)
1351
box = CGRectMakeFromPython(media_box)
1353
CGContextBeginPage(self.context, box_ptr)
1355
def flush(self, end_page=True):
1358
CGContextFlush(self.context)
1360
def begin_transparency_layer(self):
1361
CGContextBeginTransparencyLayer(self.context, NULL)
1363
def end_transparency_layer(self):
1364
CGContextEndTransparencyLayer(self.context)
1366
cdef class CGBitmapContext(CGContext):
1369
def __new__(self, *args, **kwds):
1372
def __init__(self, object size_or_array, bool grey_scale=0,
1373
int bits_per_component=8, int bytes_per_row=-1,
1374
alpha_info=kCGImageAlphaPremultipliedLast):
1376
cdef int bits_per_pixel
1377
cdef CGColorSpaceRef colorspace
1380
if hasattr(size_or_array, '__array_interface__'):
1382
arr = numpy.asarray(size_or_array, order='C')
1383
typestr = arr.dtype.str
1384
if typestr != '|u1':
1385
raise ValueError("expecting an array of unsigned bytes; got %r"
1388
if len(shape) != 3 or shape[-1] not in (3, 4):
1389
raise ValueError("expecting a shape (width, height, depth) "
1390
"with depth either 3 or 4; got %r" % shape)
1391
height, width, depth = shape
1393
# Need to add an alpha channel.
1394
alpha = numpy.empty((height, width), dtype=numpy.uint8)
1396
arr = numpy.dstack([arr, alpha])
1398
ptr, readonly = arr.__array_interface__['data']
1399
dataptr = <void*><long>ptr
1401
# It's a size tuple.
1402
width, height = size_or_array
1406
alpha_info = kCGImageAlphaNone
1407
bits_per_component = 8
1409
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray)
1410
elif bits_per_component == 5:
1411
alpha_info = kCGImageAlphaNoneSkipFirst
1413
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
1414
elif bits_per_component == 8:
1415
if alpha_info not in (kCGImageAlphaNoneSkipFirst,
1416
kCGImageAlphaNoneSkipLast,
1417
kCGImageAlphaPremultipliedFirst,
1418
kCGImageAlphaPremultipliedLast,
1420
raise ValueError("not a valid alpha_info")
1422
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
1424
raise ValueError("bits_per_component must be 5 or 8")
1427
min_bytes = (width*bits_per_pixel + 7) / 8
1428
if bytes_per_row < min_bytes:
1429
bytes_per_row = min_bytes
1431
self.data = PyMem_Malloc(height*bytes_per_row)
1432
if self.data == NULL:
1433
CGColorSpaceRelease(colorspace)
1434
raise MemoryError("could not allocate memory")
1436
# Copy the data from the array.
1437
memcpy(self.data, dataptr, width*height*depth)
1439
self.context = CGBitmapContextCreate(self.data, width, height,
1440
bits_per_component, bytes_per_row, colorspace, alpha_info)
1441
CGColorSpaceRelease(colorspace)
1443
if self.context == NULL:
1444
raise RuntimeError("could not create CGBitmapContext")
1445
self.can_release = 1
1450
def __dealloc__(self):
1451
if self.context != NULL and self.can_release:
1452
CGContextRelease(self.context)
1454
if self.data != NULL:
1455
# Hmm, this could be tricky if anything in Quartz retained a
1456
# reference to self.context
1457
PyMem_Free(self.data)
1460
property alpha_info:
1462
return CGBitmapContextGetAlphaInfo(self.context)
1464
property bits_per_component:
1466
return CGBitmapContextGetBitsPerComponent(self.context)
1468
property bits_per_pixel:
1470
return CGBitmapContextGetBitsPerPixel(self.context)
1472
property bytes_per_row:
1474
return CGBitmapContextGetBytesPerRow(self.context)
1476
# property colorspace:
1477
# def __get__(self):
1478
# return CGBitmapContextGetColorSpace(self.context)
1481
return CGBitmapContextGetHeight(self.context)
1484
return CGBitmapContextGetWidth(self.context)
1486
def __getsegcount__(self, int* lenp):
1488
lenp[0] = self.height()*self.bytes_per_row
1491
def __getreadbuffer__(self, int segment, void** ptr):
1492
# ignore invalid segment; the caller can't mean anything but the only
1493
# segment available; we're all adults
1495
return self.height()*self.bytes_per_row
1497
def __getwritebuffer__(self, int segment, void** ptr):
1498
# ignore invalid segment; the caller can't mean anything but the only
1499
# segment available; we're all adults
1501
return self.height()*self.bytes_per_row
1503
def __getcharbuffer__(self, int segment, char** ptr):
1504
# ignore invalid segment; the caller can't mean anything but the only
1505
# segment available; we're all adults
1506
ptr[0] = <char*>(self.data)
1507
return self.height()*self.bytes_per_row
1509
def clear(self, object clear_color=(1.0, 1.0, 1.0, 1.0)):
1510
"""Paint over the whole image with a solid color.
1514
# Reset the transformation matrix back to the identity.
1515
CGContextConcatCTM(self.context,
1516
CGAffineTransformInvert(CGContextGetCTM(self.context)))
1518
self.set_fill_color(clear_color)
1519
CGContextFillRect(self.context, CGRectMake(0, 0, self.width(), self.height()))
1520
self.restore_state()
1522
def save(self, object filename, file_format=None, pil_options=None):
1523
""" Save the GraphicsContext to a file. Output files are always saved
1524
in RGB or RGBA format; if this GC is not in one of these formats, it is
1525
automatically converted.
1527
If filename includes an extension, the image format is inferred from it.
1528
file_format is only required if the format can't be inferred from the
1529
filename (e.g. if you wanted to save a PNG file as a .dat or .bin).
1531
filename may also be "file-like" object such as a StringIO, in which
1532
case a file_format must be supplied.
1534
pil_options is a dict of format-specific options that are passed down to
1535
the PIL image file writer. If a writer doesn't recognize an option, it
1536
is silently ignored.
1538
If the image has an alpha channel and the specified output file format
1539
does not support alpha, the image is saved in rgb24 format.
1545
raise ImportError("need PIL to save images")
1547
if self.bits_per_pixel == 32:
1548
if self.alpha_info == kCGImageAlphaPremultipliedLast:
1550
elif self.alpha_info == kCGImageAlphaPremultipliedFirst:
1553
raise ValueError("cannot save this pixel format")
1554
elif self.bits_per_pixel == 8:
1557
raise ValueError("cannot save this pixel format")
1559
img = Image.fromstring(mode, (self.width(), self.height()), self)
1561
# Check the output format to see if it can handle an alpha channel.
1562
no_alpha_formats = ('jpg', 'bmp', 'eps', 'jpeg')
1563
if ((isinstance(filename, basestring) and
1564
os.path.splitext(filename)[1][1:] in no_alpha_formats) or
1565
(file_format.lower() in no_alpha_formats)):
1566
img = img.convert('RGB')
1568
img.save(filename, format=file_format, options=pil_options)
1571
cdef CGImageRef image
1573
cdef readonly c_numpy.ndarray bmp_array
1575
def __new__(self, *args, **kwds):
1580
return CGImageGetWidth(self.image)
1584
return CGImageGetHeight(self.image)
1586
property bits_per_component:
1588
return CGImageGetBitsPerComponent(self.image)
1590
property bits_per_pixel:
1592
return CGImageGetBitsPerPixel(self.image)
1594
property bytes_per_row:
1596
return CGImageGetBytesPerRow(self.image)
1598
property alpha_info:
1600
return CGImageGetAlphaInfo(self.image)
1602
property should_interpolate:
1604
return CGImageGetShouldInterpolate(self.image)
1608
return CGImageIsMask(self.image)
1610
def __init__(self, object size_or_array, bool grey_scale=0,
1611
int bits_per_component=8, int bytes_per_row=-1,
1612
alpha_info=kCGImageAlphaLast, int should_interpolate=1):
1614
cdef int bits_per_pixel
1615
cdef CGColorSpaceRef colorspace
1617
if hasattr(size_or_array, '__array_interface__'):
1620
typestr = arr.__array_interface__['typestr']
1621
if typestr != '|u1':
1622
raise ValueError("expecting an array of unsigned bytes; got %r"
1624
shape = arr.__array_interface__['shape']
1626
if (len(shape) == 3 and shape[-1] != 1) or (len(shape) != 2):
1627
raise ValueError("with grey_scale, expecting a shape "
1628
"(height, width) or (height, width, 1); got %r" % shape)
1629
height, width = shape[:2]
1632
if len(shape) != 3 or shape[-1] not in (3, 4):
1633
raise ValueError("expecting a shape (height, width, depth) "
1634
"with depth either 3 or 4; got %r" % shape)
1635
height, width, depth = shape
1637
alpha_info = kCGImageAlphaNone
1640
arr = numpy.array(arr)
1641
alpha_info = kCGImageAlphaPremultipliedLast
1643
# It's a size tuple.
1644
width, height = size_or_array
1647
alpha_info = kCGImageAlphaNone
1650
alpha_info = kCGImageAlphaPremultipliedLast
1651
arr = numpy.zeros((height, width, lastdim), dtype=numpy.uint8)
1653
self.bmp_array = <c_numpy.ndarray>arr
1654
Py_INCREF(self.bmp_array)
1655
self.data = <void*>c_numpy.PyArray_DATA(self.bmp_array)
1658
alpha_info = kCGImageAlphaNone
1659
bits_per_component = 8
1661
colorspace = CGColorSpaceCreateDeviceGray()
1662
elif bits_per_component == 5:
1663
alpha_info = kCGImageAlphaNoneSkipFirst
1665
colorspace = CGColorSpaceCreateDeviceRGB()
1666
elif bits_per_component == 8:
1667
if alpha_info in (kCGImageAlphaNoneSkipFirst,
1668
kCGImageAlphaNoneSkipLast,
1669
kCGImageAlphaPremultipliedFirst,
1670
kCGImageAlphaPremultipliedLast,
1675
elif alpha_info == kCGImageAlphaNone:
1677
colorspace = CGColorSpaceCreateDeviceRGB()
1679
raise ValueError("bits_per_component must be 5 or 8")
1682
min_bytes = (width*bits_per_pixel + 7) / 8
1683
if bytes_per_row < min_bytes:
1684
bytes_per_row = min_bytes
1686
cdef CGDataProviderRef provider
1687
provider = CGDataProviderCreateWithData(
1688
NULL, self.data, c_numpy.PyArray_SIZE(self.bmp_array), NULL)
1689
if provider == NULL:
1690
raise RuntimeError("could not make provider")
1692
cdef CGColorSpaceRef space
1693
space = CGColorSpaceCreateDeviceRGB()
1695
self.image = CGImageCreate(width, height, bits_per_component,
1696
bits_per_pixel, bytes_per_row, space, alpha_info, provider, NULL,
1697
should_interpolate, kCGRenderingIntentDefault)
1698
CGColorSpaceRelease(space)
1699
CGDataProviderRelease(provider)
1701
if self.image == NULL:
1702
raise RuntimeError("could not make image")
1704
def __dealloc__(self):
1705
if self.image != NULL:
1706
CGImageRelease(self.image)
1708
Py_XDECREF(self.bmp_array)
1710
cdef class CGImageFile(CGImage):
1711
def __init__(self, object image_or_filename, int should_interpolate=1):
1712
cdef int width, height, bits_per_component, bits_per_pixel, bytes_per_row
1713
cdef CGImageAlphaInfo alpha_info
1718
if type(image_or_filename) is str:
1719
img = Image.open(image_or_filename)
1721
elif isinstance(image_or_filename, Image.Image):
1722
img = image_or_filename
1724
raise ValueError("need a PIL Image or a filename")
1726
width, height = img.size
1729
if mode not in ["L", "RGB","RGBA"]:
1730
img = img.convert(mode="RGBA")
1733
bits_per_component = 8
1737
alpha_info = kCGImageAlphaNone
1738
elif mode == 'RGBA':
1740
alpha_info = kCGImageAlphaPremultipliedLast
1743
alpha_info = kCGImageAlphaNone
1745
bytes_per_row = (bits_per_pixel*width + 7)/ 8
1752
dims[2] = bits_per_pixel/bits_per_component
1754
self.bmp_array = c_numpy.PyArray_SimpleNew(3, &(dims[0]), NPY_UBYTE)
1756
data = self.bmp_array.data
1758
py_data = PyString_AsString(s)
1760
memcpy(<void*>data, <void*>py_data, len(s))
1764
cdef CGDataProviderRef provider
1765
provider = CGDataProviderCreateWithData(
1766
NULL, <void*>data, len(data), NULL)
1768
if provider == NULL:
1769
raise RuntimeError("could not make provider")
1771
cdef CGColorSpaceRef space
1772
space = CGColorSpaceCreateDeviceRGB()
1774
self.image = CGImageCreate(width, height, bits_per_component,
1775
bits_per_pixel, bytes_per_row, space, alpha_info, provider, NULL,
1776
should_interpolate, kCGRenderingIntentDefault)
1777
CGColorSpaceRelease(space)
1778
CGDataProviderRelease(provider)
1780
if self.image == NULL:
1781
raise RuntimeError("could not make image")
1783
def __dealloc__(self):
1784
if self.image != NULL:
1785
CGImageRelease(self.image)
1787
Py_XDECREF(self.bmp_array)
1789
cdef class CGImageMask(CGImage):
1790
def __init__(self, char* data, int width, int height,
1791
int bits_per_component, int bits_per_pixel, int bytes_per_row,
1792
int should_interpolate=1):
1794
cdef CGDataProviderRef provider
1795
provider = CGDataProviderCreateWithData(
1796
NULL, <void*>data, len(data), NULL)
1798
if provider == NULL:
1799
raise RuntimeError("could not make provider")
1801
self.image = CGImageMaskCreate(width, height, bits_per_component,
1802
bits_per_pixel, bytes_per_row, provider, NULL,
1804
CGDataProviderRelease(provider)
1806
if self.image == NULL:
1807
raise RuntimeError("could not make image")
1809
cdef class CGPDFDocument:
1810
cdef CGPDFDocumentRef document
1812
property number_of_pages:
1814
return CGPDFDocumentGetNumberOfPages(self.document)
1816
property allows_copying:
1818
return CGPDFDocumentAllowsCopying(self.document)
1820
property allows_printing:
1822
return CGPDFDocumentAllowsPrinting(self.document)
1824
property is_encrypted:
1826
return CGPDFDocumentIsEncrypted(self.document)
1828
property is_unlocked:
1830
return CGPDFDocumentIsUnlocked(self.document)
1832
def __init__(self, char* filename):
1834
if not os.path.exists(filename) or not os.path.isfile(filename):
1835
raise ValueError("%s is not a file" % filename)
1838
cfurl = url_from_filename(filename)
1840
self.document = CGPDFDocumentCreateWithURL(cfurl)
1842
if self.document == NULL:
1843
raise RuntimeError("could not create CGPDFDocument")
1845
def unlock_with_password(self, char* password):
1846
return CGPDFDocumentUnlockWithPassword(self.document, password)
1848
def get_media_box(self, int page):
1850
cgrect = CGPDFDocumentGetMediaBox(self.document, page)
1851
return (cgrect.origin.x, cgrect.origin.y,
1852
cgrect.size.width, cgrect.size.height)
1854
def get_crop_box(self, int page):
1856
cgrect = CGPDFDocumentGetCropBox(self.document, page)
1857
return (cgrect.origin.x, cgrect.origin.y,
1858
cgrect.size.width, cgrect.size.height)
1860
def get_bleed_box(self, int page):
1862
cgrect = CGPDFDocumentGetBleedBox(self.document, page)
1863
return (cgrect.origin.x, cgrect.origin.y,
1864
cgrect.size.width, cgrect.size.height)
1866
def get_trim_box(self, int page):
1868
cgrect = CGPDFDocumentGetTrimBox(self.document, page)
1869
return (cgrect.origin.x, cgrect.origin.y,
1870
cgrect.size.width, cgrect.size.height)
1872
def get_art_box(self, int page):
1874
cgrect = CGPDFDocumentGetArtBox(self.document, page)
1875
return (cgrect.origin.x, cgrect.origin.y,
1876
cgrect.size.width, cgrect.size.height)
1878
def get_rotation_angle(self, int page):
1880
angle = CGPDFDocumentGetRotationAngle(self.document, page)
1882
raise ValueError("page %d does not exist" % page)
1884
def __dealloc__(self):
1885
if self.document != NULL:
1886
CGPDFDocumentRelease(self.document)
1887
self.document = NULL
1889
cdef CGDataProviderRef CGDataProviderFromFilename(char* string) except NULL:
1891
cdef CGDataProviderRef result
1893
cfurl = url_from_filename(string)
1895
raise RuntimeError("could not create CFURLRef")
1897
result = CGDataProviderCreateWithURL(cfurl)
1900
raise RuntimeError("could not create CGDataProviderRef")
1903
cdef class CGAffine:
1904
cdef CGAffineTransform real_transform
1908
return self.real_transform.a
1909
def __set__(self, float value):
1910
self.real_transform.a = value
1914
return self.real_transform.b
1915
def __set__(self, float value):
1916
self.real_transform.b = value
1920
return self.real_transform.c
1921
def __set__(self, float value):
1922
self.real_transform.c = value
1926
return self.real_transform.d
1927
def __set__(self, float value):
1928
self.real_transform.d = value
1932
return self.real_transform.tx
1933
def __set__(self, float value):
1934
self.real_transform.tx = value
1938
return self.real_transform.ty
1939
def __set__(self, float value):
1940
self.real_transform.ty = value
1942
def __init__(self, float a=1.0, float b=0.0, float c=0.0, float d=1.0,
1943
float tx=0.0, float ty=0.0):
1944
self.real_transform = CGAffineTransformMake(a,b,c,d,tx,ty)
1946
def translate(self, float tx, float ty):
1947
self.real_transform = CGAffineTransformTranslate(self.real_transform,
1951
def rotate(self, float angle):
1952
self.real_transform = CGAffineTransformRotate(self.real_transform,
1956
def scale(self, float sx, float sy):
1957
self.real_transform = CGAffineTransformScale(self.real_transform, sx,
1962
self.real_transform = CGAffineTransformInvert(self.real_transform)
1965
def concat(self, CGAffine other not None):
1966
self.real_transform = CGAffineTransformConcat(self.real_transform,
1967
other.real_transform)
1970
def __mul__(CGAffine x not None, CGAffine y not None):
1971
cdef CGAffineTransform new_transform
1972
new_transform = CGAffineTransformConcat(x.real_transform,
1974
new_affine = CGAffine()
1975
set_affine_transform(new_affine, new_transform)
1978
cdef void init_from_cgaffinetransform(self, CGAffineTransform t):
1979
self.real_transform = t
1981
def __div__(CGAffine x not None, CGAffine y not None):
1982
cdef CGAffineTransform new_transform
1983
new_transform = CGAffineTransformInvert(y.real_transform)
1984
new_affine = CGAffine()
1985
set_affine_transform(new_affine, CGAffineTransformConcat(x.real_transform, new_transform))
1988
def apply_to_point(self, float x, float y):
1989
cdef CGPoint oldpoint
1990
oldpoint = CGPointMake(x, y)
1991
cdef CGPoint newpoint
1992
newpoint = CGPointApplyAffineTransform(oldpoint,
1993
self.real_transform)
1994
return newpoint.x, newpoint.y
1996
def apply_to_size(self, float width, float height):
1998
oldsize = CGSizeMake(width, height)
2000
newsize = CGSizeApplyAffineTransform(oldsize, self.real_transform)
2001
return newsize.width, newsize.height
2004
return "CGAffine(%r, %r, %r, %r, %r, %r)" % (self.a, self.b, self.c,
2005
self.d, self.tx, self.ty)
2007
def as_matrix(self):
2008
return ((self.a, self.b, 0.0),
2009
(self.c, self.d, 0.0),
2010
(self.tx,self.ty,1.0))
2012
cdef set_affine_transform(CGAffine t, CGAffineTransform newt):
2013
t.init_from_cgaffinetransform(newt)
2016
## cdef CGPoint real_point
2019
## def __get__(self):
2020
## return self.real_point.x
2021
## def __set__(self, float value):
2022
## self.real_point.x = value
2025
## def __get__(self):
2026
## return self.real_point.y
2027
## def __set__(self, float value):
2028
## self.real_point.y = value
2030
## def __init__(self, float x, float y):
2031
## self.real_point = CGPointMake(x, y)
2033
## def apply_transform(self, CGAffine transform not None):
2034
## self.real_point = CGPointApplyTransform(self.real_point,
2035
## transform.real_transform)
2038
cdef CGRect real_rect
2042
return self.real_rect.origin.x
2043
def __set__(self, float value):
2044
self.real_rect.origin.x = value
2048
return self.real_rect.origin.y
2049
def __set__(self, float value):
2050
self.real_rect.origin.y = value
2054
return self.real_rect.size.width
2055
def __set__(self, float value):
2056
self.real_rect.size.width = value
2060
return self.real_rect.size.height
2061
def __set__(self, float value):
2062
self.real_rect.size.height = value
2066
return CGRectGetMinX(self.real_rect)
2070
return CGRectGetMaxX(self.real_rect)
2074
return CGRectGetMinY(self.real_rect)
2078
return CGRectGetMaxY(self.real_rect)
2082
return CGRectGetMidX(self.real_rect)
2086
return CGRectGetMidY(self.real_rect)
2090
return CGRectIsNull(self.real_rect)
2094
return CGRectIsEmpty(self. real_rect)
2096
def __init__(self, float x=0.0, float y=0.0, float width=0.0, float
2098
self.real_rect = CGRectMake(x,y,width,height)
2100
def intersects(self, Rect other not None):
2101
return CGRectIntersectsRect(self.real_rect, other.real_rect)
2103
def contains_rect(self, Rect other not None):
2104
return CGRectContainsRect(self.real_rect, other.real_rect)
2106
def contains_point(self, float x, float y):
2107
return CGRectContainsPoint(self.real_rect, CGPointMake(x,y))
2109
def __richcmp__(Rect x not None, Rect y not None, int op):
2111
return CGRectEqualToRect(x.real_rect, y.real_rect)
2113
return not CGRectEqualToRect(x.real_rect, y.real_rect)
2115
raise NotImplementedError("only (in)equality can be tested")
2117
def standardize(self):
2118
self.real_rect = CGRectStandardize(self.real_rect)
2121
def inset(self, float x, float y):
2122
cdef CGRect new_rect
2123
new_rect = CGRectInset(self.real_rect, x, y)
2125
set_rect(rect, new_rect)
2128
def offset(self, float x, float y):
2129
cdef CGRect new_rect
2130
new_rect = CGRectOffset(self.real_rect, x, y)
2132
set_rect(rect, new_rect)
2136
self.real_rect = CGRectIntegral(self.real_rect)
2139
def __add__(Rect x not None, Rect y not None):
2140
cdef CGRect new_rect
2141
new_rect = CGRectUnion(x.real_rect, y.real_rect)
2143
set_rect(rect, new_rect)
2146
def union(self, Rect other not None):
2147
cdef CGRect new_rect
2148
new_rect = CGRectUnion(self.real_rect, other.real_rect)
2150
set_rect(rect, new_rect)
2153
def intersection(self, Rect other not None):
2154
cdef CGRect new_rect
2155
new_rect = CGRectIntersection(self.real_rect, other.real_rect)
2157
set_rect(rect, new_rect)
2160
def divide(self, float amount, edge):
2162
cdef CGRect remainder
2163
CGRectDivide(self.real_rect, &slice, &remainder, amount, edge)
2165
set_rect(pyslice, slice)
2167
set_rect(pyrem, remainder)
2168
return pyslice, pyrem
2170
cdef init_from_cgrect(self, CGRect cgrect):
2171
self.real_rect = cgrect
2174
return "Rect(%r, %r, %r, %r)" % (self.x, self.y, self.width,
2177
cdef set_rect(Rect pyrect, CGRect cgrect):
2178
pyrect.init_from_cgrect(cgrect)
2180
cdef class CGMutablePath:
2181
cdef CGMutablePathRef path
2183
def __init__(self, CGMutablePath path=None):
2184
if path is not None:
2185
self.path = CGPathCreateMutableCopy(path.path)
2187
self.path = CGPathCreateMutable()
2189
def begin_path(self):
2192
def move_to(self, float x, float y, CGAffine transform=None):
2193
cdef CGAffineTransform *ptr
2195
if transform is not None:
2196
ptr = &(transform.real_transform)
2197
CGPathMoveToPoint(self.path, ptr, x, y)
2199
def arc(self, float x, float y, float r, float startAngle, float endAngle,
2200
bool clockwise=False, CGAffine transform=None):
2202
cdef CGAffineTransform *ptr
2204
if transform is not None:
2205
ptr = &(transform.real_transform)
2207
CGPathAddArc(self.path, ptr, x, y, r, startAngle, endAngle, clockwise)
2209
def arc_to(self, float x1, float y1, float x2, float y2, float r,
2210
CGAffine transform=None):
2212
cdef CGAffineTransform *ptr
2214
if transform is not None:
2215
ptr = &(transform.real_transform)
2217
CGPathAddArcToPoint(self.path, ptr, x1,y1, x2,y2, r)
2219
def curve_to(self, float cx1, float cy1, float cx2, float cy2, float x,
2220
float y, CGAffine transform=None):
2222
cdef CGAffineTransform *ptr
2224
if transform is not None:
2225
ptr = &(transform.real_transform)
2227
CGPathAddCurveToPoint(self.path, ptr, cx1, cy1, cx2, cy2, x, y)
2229
def line_to(self, float x, float y, CGAffine transform=None):
2230
cdef CGAffineTransform *ptr
2232
if transform is not None:
2233
ptr = &(transform.real_transform)
2235
CGPathAddLineToPoint(self.path, ptr, x, y)
2237
def lines(self, points, CGAffine transform=None):
2238
cdef CGAffineTransform *ptr
2240
if transform is not None:
2241
ptr = &(transform.real_transform)
2247
CGPathMoveToPoint(self.path, ptr, points[0][0], points[0][1])
2249
for i from 1 <= i < n:
2250
CGPathAddLineToPoint(self.path, ptr, points[i][0], points[i][1])
2252
def add_path(self, CGMutablePath other_path not None, CGAffine transform=None):
2253
cdef CGAffineTransform *ptr
2255
if transform is not None:
2256
ptr = &(transform.real_transform)
2258
CGPathAddPath(self.path, ptr, other_path.path)
2260
def quad_curve_to(self, float cx, float cy, float x, float y, CGAffine transform=None):
2261
cdef CGAffineTransform *ptr
2263
if transform is not None:
2264
ptr = &(transform.real_transform)
2266
CGPathAddQuadCurveToPoint(self.path, ptr, cx, cy, x, y)
2268
def rect(self, float x, float y, float sx, float sy, CGAffine transform=None):
2269
cdef CGAffineTransform *ptr
2271
if transform is not None:
2272
ptr = &(transform.real_transform)
2274
CGPathAddRect(self.path, ptr, CGRectMake(x,y,sx,sy))
2276
def rects(self, rects, CGAffine transform=None):
2277
cdef CGAffineTransform *ptr
2279
if transform is not None:
2280
ptr = &(transform.real_transform)
2285
for i from 0 <= i < n:
2286
CGPathAddRect(self.path, ptr, CGRectMakeFromPython(rects[i]))
2288
def close_path(self):
2289
CGPathCloseSubpath(self.path)
2292
return CGPathIsEmpty(self.path)
2294
def get_current_point(self):
2296
point = CGPathGetCurrentPoint(self.path)
2297
return point.x, point.y
2299
def get_bounding_box(self):
2301
rect = CGPathGetBoundingBox(self.path)
2302
return (rect.origin.x, rect.origin.y,
2303
rect.size.width, rect.size.height)
2305
def __richcmp__(CGMutablePath x not None, CGMutablePath y not None, int op):
2307
# testing for equality
2308
return CGPathEqualToPath(x.path, y.path)
2310
# testing for inequality
2311
return not CGPathEqualToPath(x.path, y.path)
2313
raise NotImplementedError("only (in)equality tests are allowed")
2315
def __dealloc__(self):
2316
if self.path != NULL:
2317
CGPathRelease(self.path)
2320
cdef class _Markers:
2322
def get_marker(self, int marker_type, float size=1.0):
2323
""" Return the CGMutablePath corresponding to the given marker
2326
Marker.get_marker(marker_type, size=1.0)
2331
One of the enumerated marker types in enthought.kiva.constants.
2332
size : float, optional
2333
The linear size in points of the marker. Some markers (e.g. dot)
2338
path : CGMutablePath
2341
if marker_type == constants.NO_MARKER:
2342
return CGMutablePath()
2343
elif marker_type == constants.SQUARE_MARKER:
2344
return self.square(size)
2345
elif marker_type == constants.DIAMOND_MARKER:
2346
return self.diamond(size)
2347
elif marker_type == constants.CIRCLE_MARKER:
2348
return self.circle(size)
2349
elif marker_type == constants.CROSSED_CIRCLE_MARKER:
2350
raise NotImplementedError
2351
elif marker_type == constants.CROSS_MARKER:
2352
return self.cross(size)
2353
elif marker_type == constants.TRIANGLE_MARKER:
2354
raise NotImplementedError
2355
elif marker_type == constants.INVERTED_TRIANGLE_MARKER:
2356
raise NotImplementedError
2357
elif marker_type == constants.PLUS_MARKER:
2358
raise NotImplementedError
2359
elif marker_type == constants.DOT_MARKER:
2360
raise NotImplementedError
2361
elif marker_type == constants.PIXEL_MARKER:
2362
raise NotImplementedError
2365
def square(self, float size):
2370
m.rect(-half,-half,size,size)
2373
def diamond(self, float size):
2378
m.move_to(0.0, -half)
2379
m.line_to(-half, 0.0)
2380
m.line_to(0.0, half)
2381
m.line_to(half, 0.0)
2385
def x(self, float size):
2390
m.move_to(-half,-half)
2391
m.line_to(half,half)
2392
m.move_to(-half,half)
2393
m.line_to(half,-half)
2396
def cross(self, float size):
2401
m.move_to(0.0, -half)
2402
m.line_to(0.0, half)
2403
m.move_to(-half, 0.0)
2404
m.line_to(half, 0.0)
2409
m.rect(-0.5,-0.5,1.0,1.0)
2412
def circle(self, float size):
2416
m.arc(0.0, 0.0, half, 0.0, 6.2831853071795862, 1)
2419
Markers = _Markers()
2421
cdef class ShadingFunction:
2422
cdef CGFunctionRef function
2424
cdef void _setup_function(self, CGFunctionEvaluateCallback callback):
2426
cdef CGFunctionCallbacks callbacks
2427
callbacks.version = 0
2428
callbacks.releaseInfo = NULL
2429
callbacks.evaluate = <CGFunctionEvaluateCallback>callback
2431
cdef float domain_bounds[2]
2432
cdef float range_bounds[8]
2434
domain_bounds[0] = 0.0
2435
domain_bounds[1] = 1.0
2436
for i from 0 <= i < 4:
2437
range_bounds[2*i] = 0.0
2438
range_bounds[2*i+1] = 1.0
2440
self.function = CGFunctionCreate(<void*>self, 1, domain_bounds,
2441
4, range_bounds, &callbacks)
2442
if self.function == NULL:
2443
raise RuntimeError("could not make CGFunctionRef")
2445
cdef void shading_callback(object self, float* in_data, float* out_data):
2447
out = self(in_data[0])
2448
for i from 0 <= i < self.n_dims:
2452
cdef CGShadingRef shading
2453
cdef public object function
2456
def __init__(self, ShadingFunction func not None):
2457
raise NotImplementedError("use AxialShading or RadialShading")
2459
def __dealloc__(self):
2460
if self.shading != NULL:
2461
CGShadingRelease(self.shading)
2463
cdef class AxialShading(Shading):
2464
def __init__(self, ShadingFunction func not None, object start, object end,
2465
int extend_start=0, int extend_end=0):
2469
cdef CGPoint start_point, end_point
2470
start_point = CGPointMake(start[0], start[1])
2471
end_point = CGPointMake(end[0], end[1])
2473
self.function = func
2475
cdef CGColorSpaceRef space
2476
space = CGColorSpaceCreateDeviceRGB()
2477
self.shading = CGShadingCreateAxial(space, start_point, end_point,
2478
func.function, extend_start, extend_end)
2479
CGColorSpaceRelease(space)
2480
if self.shading == NULL:
2481
raise RuntimeError("could not make CGShadingRef")
2483
cdef class RadialShading(Shading):
2484
def __init__(self, ShadingFunction func not None, object start,
2485
float start_radius, object end, float end_radius, int extend_start=0,
2490
cdef CGPoint start_point, end_point
2491
start_point = CGPointMake(start[0], start[1])
2492
end_point = CGPointMake(end[0], end[1])
2494
self.function = func
2496
cdef CGColorSpaceRef space
2497
space = CGColorSpaceCreateDeviceRGB()
2498
self.shading = CGShadingCreateRadial(space, start_point, start_radius,
2499
end_point, end_radius, func.function, extend_start, extend_end)
2500
CGColorSpaceRelease(space)
2501
if self.shading == NULL:
2502
raise RuntimeError("could not make CGShadingRef")
2504
cdef void safe_free(void* mem):
2508
cdef class PiecewiseLinearColorFunction(ShadingFunction):
2516
def __init__(self, object stop_colors):
2517
cdef c_numpy.ndarray stop_array
2520
stop_colors = numpy.array(stop_colors).astype(numpy.float32)
2522
if not (4 <= stop_colors.shape[0] <= 5) or len(stop_colors.shape) != 2:
2523
raise ValueError("need array [stops, red, green, blue[, alpha]]")
2525
if stop_colors[0,0] != 0.0 or stop_colors[0,-1] != 1.0:
2526
raise ValueError("stops need to start with 0.0 and end with 1.0")
2528
if not numpy.greater_equal(numpy.diff(stop_colors[0]), 0.0).all():
2529
raise ValueError("stops must be sorted and unique")
2531
self.num_stops = stop_colors.shape[1]
2532
self.stops = <float*>PyMem_Malloc(sizeof(float)*self.num_stops)
2533
self.red = <float*>PyMem_Malloc(sizeof(float)*self.num_stops)
2534
self.green = <float*>PyMem_Malloc(sizeof(float)*self.num_stops)
2535
self.blue = <float*>PyMem_Malloc(sizeof(float)*self.num_stops)
2536
self.alpha = <float*>PyMem_Malloc(sizeof(float)*self.num_stops)
2538
has_alpha = stop_colors.shape[0] == 5
2539
for i from 0 <= i < self.num_stops:
2540
self.stops[i] = stop_colors[0,i]
2541
self.red[i] = stop_colors[1,i]
2542
self.green[i] = stop_colors[2,i]
2543
self.blue[i] = stop_colors[3,i]
2545
self.alpha[i] = stop_colors[4,i]
2549
self._setup_function(piecewise_callback)
2553
print 'PiecewiseLinearColorFunction'
2554
print ' num_stops = %i' % self.num_stops
2556
for i from 0 <= i < self.num_stops:
2557
print self.stops[i],
2560
for i from 0 <= i < self.num_stops:
2564
for i from 0 <= i < self.num_stops:
2565
print self.green[i],
2568
for i from 0 <= i < self.num_stops:
2572
for i from 0 <= i < self.num_stops:
2573
print self.alpha[i],
2576
def __dealloc__(self):
2577
safe_free(self.stops)
2579
safe_free(self.green)
2580
safe_free(self.blue)
2581
safe_free(self.alpha)
2584
cdef int bisect_left(PiecewiseLinearColorFunction self, float t):
2585
cdef int lo, hi, mid
2592
stop = self.stops[mid]
2599
cdef void piecewise_callback(void* obj, float* t, float* out):
2602
cdef PiecewiseLinearColorFunction self
2604
self = <PiecewiseLinearColorFunction>obj
2608
if fabs(t[0]) < eps:
2609
out[0] = self.red[0]
2610
out[1] = self.green[0]
2611
out[2] = self.blue[0]
2612
out[3] = self.alpha[0]
2614
if fabs(t[0] - 1.0) < eps:
2615
i = self.num_stops - 1
2616
out[0] = self.red[i]
2617
out[1] = self.green[i]
2618
out[2] = self.blue[i]
2619
out[3] = self.alpha[i]
2622
i = bisect_left(self, t[0])
2625
dx = self.stops[i] - self.stops[i-1]
2628
f = (t[0]-self.stops[i-1])/dx
2634
out[0] = f*self.red[i] + g*self.red[i-1]
2635
out[1] = f*self.green[i] + g*self.green[i-1]
2636
out[2] = f*self.blue[i] + g*self.blue[i-1]
2637
out[3] = f*self.alpha[i] + g*self.alpha[i-1]
2640
#### Font utilities ####
2642
cdef ATSUStyle _create_atsu_style(object postscript_name, float font_size):
2644
cdef ATSUStyle atsu_style
2645
cdef ATSUFontID atsu_font
2646
cdef Fixed atsu_size
2647
cdef char* c_ps_name
2649
# Create the attribute arrays.
2650
cdef ATSUAttributeTag attr_tags[2]
2651
cdef ByteCount attr_sizes[2]
2652
cdef ATSUAttributeValuePtr attr_values[2]
2657
atsu_size = FloatToFixed(font_size)
2659
# Look up the ATSUFontID for the given PostScript name of the font.
2660
postscript_name = postscript_name.encode('utf-8')
2661
c_ps_name = PyString_AsString(postscript_name)
2662
err = ATSUFindFontFromName(<void*>c_ps_name, len(postscript_name),
2663
kFontPostscriptName, kFontNoPlatformCode, kFontNoScriptCode,
2664
kFontNoLanguageCode, &atsu_font)
2668
# Set the ATSU font in the attribute arrays.
2669
attr_tags[0] = kATSUFontTag
2670
attr_sizes[0] = sizeof(ATSUFontID)
2671
attr_values[0] = &atsu_font
2673
# Set the font size in the attribute arrays.
2674
attr_tags[1] = kATSUSizeTag
2675
attr_sizes[1] = sizeof(Fixed)
2676
attr_values[1] = &atsu_size
2678
# Create the ATSU style.
2679
err = ATSUCreateStyle(&atsu_style)
2681
if atsu_style != NULL:
2682
ATSUDisposeStyle(atsu_style)
2685
# Set the style attributes.
2686
err = ATSUSetAttributes(atsu_style, 2, attr_tags, attr_sizes, attr_values)
2688
if atsu_style != NULL:
2689
ATSUDisposeStyle(atsu_style)
2695
cdef object _create_atsu_layout(object the_string, ATSUStyle style, ATSUTextLayout* layout):
2696
cdef ATSUTextLayout atsu_layout
2697
cdef CFIndex text_length
2699
cdef UniChar *uni_buffer
2700
cdef CFRange uni_range
2701
cdef CFStringRef cf_string
2704
layout[0] = atsu_layout = NULL
2705
if len(the_string) == 0:
2709
the_string = the_string.encode('utf-8')
2710
c_string = PyString_AsString(the_string)
2712
cf_string = CFStringCreateWithCString(NULL, c_string, kCFStringEncodingUTF8)
2713
text_length = CFStringGetLength(cf_string)
2715
# Extract the Unicode data from the CFStringRef.
2716
uni_range = CFRangeMake(0, text_length)
2717
uni_buffer = <UniChar*>PyMem_Malloc(text_length * sizeof(UniChar))
2718
if uni_buffer == NULL:
2719
raise MemoryError("could not allocate %d bytes of memory" % (text_length * sizeof(UniChar)))
2720
CFStringGetCharacters(cf_string, uni_range, uni_buffer)
2722
# Create the ATSUI layout.
2723
err = ATSUCreateTextLayoutWithTextPtr(<ConstUniCharArrayPtr>uni_buffer, 0, text_length, text_length, 1, <UniCharCount*>&text_length, &style, &atsu_layout)
2725
PyMem_Free(uni_buffer)
2726
raise RuntimeError("could not create an ATSUI layout")
2728
layout[0] = atsu_layout
2732
cdef object _set_cgcontext_for_layout(CGContextRef context, ATSUTextLayout layout):
2733
cdef ATSUAttributeTag tag
2735
cdef ATSUAttributeValuePtr value
2738
tag = kATSUCGContextTag
2739
size = sizeof(CGContextRef)
2742
err = ATSUSetLayoutControls(layout, 1, &tag, &size, &value)
2744
raise RuntimeError("could not assign the CGContextRef to the ATSUTextLayout")
2747
#### EOF #######################################################################