rubyquartz 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYRIGHT +24 -0
- data/History.txt +9 -0
- data/Manifest.txt +95 -0
- data/README.txt +71 -0
- data/Rakefile +51 -0
- data/ext/ExportedSymbols.txt +1 -0
- data/ext/bitmap.c +129 -0
- data/ext/bitmap.h +38 -0
- data/ext/bitmap_context.c +67 -0
- data/ext/bitmap_context.h +31 -0
- data/ext/color.c +145 -0
- data/ext/color.h +34 -0
- data/ext/colorspace.c +94 -0
- data/ext/colorspace.h +33 -0
- data/ext/context.c +393 -0
- data/ext/context.h +33 -0
- data/ext/extconf.rb +48 -0
- data/ext/font.c +148 -0
- data/ext/font.h +32 -0
- data/ext/function.c +124 -0
- data/ext/function.h +32 -0
- data/ext/image.c +140 -0
- data/ext/image.h +31 -0
- data/ext/image_source.c +143 -0
- data/ext/image_source.h +31 -0
- data/ext/pdf_document.c +91 -0
- data/ext/pdf_document.h +31 -0
- data/ext/pdf_page.c +79 -0
- data/ext/pdf_page.h +31 -0
- data/ext/point.c +42 -0
- data/ext/point.h +32 -0
- data/ext/rect.c +45 -0
- data/ext/rect.h +32 -0
- data/ext/rubyquartz.c +80 -0
- data/ext/rubyquartz.h +29 -0
- data/ext/rubyquartz_prefix.h +45 -0
- data/ext/shading.c +68 -0
- data/ext/shading.h +32 -0
- data/ext/size.c +42 -0
- data/ext/size.h +32 -0
- data/ext/text.c +258 -0
- data/ext/text.h +39 -0
- data/ext/utilities.c +181 -0
- data/ext/utilities.h +43 -0
- data/lib/rubyquartz.rb +59 -0
- data/lib/rubyquartz/bitmap.rb +62 -0
- data/lib/rubyquartz/bitmap_context.rb +50 -0
- data/lib/rubyquartz/color.rb +68 -0
- data/lib/rubyquartz/colorspace.rb +42 -0
- data/lib/rubyquartz/context.rb +116 -0
- data/lib/rubyquartz/font.rb +48 -0
- data/lib/rubyquartz/function.rb +57 -0
- data/lib/rubyquartz/image.rb +48 -0
- data/lib/rubyquartz/image_source.rb +50 -0
- data/lib/rubyquartz/pdf_document.rb +47 -0
- data/lib/rubyquartz/pdf_page.rb +54 -0
- data/lib/rubyquartz/point.rb +63 -0
- data/lib/rubyquartz/rect.rb +127 -0
- data/lib/rubyquartz/shading.rb +48 -0
- data/lib/rubyquartz/size.rb +39 -0
- data/lib/rubyquartz/text.rb +50 -0
- data/test/images/TestContext.test_begin_path.png +0 -0
- data/test/images/TestContext.test_draw_image.png +0 -0
- data/test/images/TestContext.test_draw_path.png +0 -0
- data/test/images/TestContext.test_draw_pdf.png +0 -0
- data/test/images/TestContext.test_draw_shading_axial.png +0 -0
- data/test/images/TestContext.test_draw_text.png +0 -0
- data/test/images/TestContext.test_draw_text_baseline_aligned.png +0 -0
- data/test/images/TestContext.test_draw_text_flipped.png +0 -0
- data/test/images/TestContext.test_draw_text_flipped_non_zero_point.png +0 -0
- data/test/images/TestContext.test_draw_text_non_zero_point.png +0 -0
- data/test/images/TestContext.test_fill.png +0 -0
- data/test/images/TestContext.test_gsave.png +0 -0
- data/test/images/TestContext.test_gsave_block.png +0 -0
- data/test/images/TestContext.test_line_width.png +0 -0
- data/test/images/TestContext.test_lineto.png +0 -0
- data/test/images/TestContext.test_rotate.png +0 -0
- data/test/images/TestContext.test_rounded_rect.png +0 -0
- data/test/images/TestContext.test_scale.png +0 -0
- data/test/images/TestContext.test_set_line_dash.png +0 -0
- data/test/images/TestContext.test_shadow.png +0 -0
- data/test/images/TestContext.test_stroke_rect.png +0 -0
- data/test/images/TestContext.test_text_bounds.png +0 -0
- data/test/images/TestContext.test_translate.png +0 -0
- data/test/inputs/circle.pdf +0 -0
- data/test/inputs/wash.png +0 -0
- data/test/rubyquartz_test.rb +51 -0
- data/test/tc_bitmap_context.rb +43 -0
- data/test/tc_color.rb +43 -0
- data/test/tc_context.rb +315 -0
- data/test/tc_font.rb +54 -0
- data/test/tc_image_source.rb +69 -0
- data/test/tc_pdf_document.rb +63 -0
- data/test/tc_pdf_page.rb +53 -0
- data/test/tc_text.rb +39 -0
- metadata +156 -0
data/ext/text.c
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2006, The Omni Group. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Redistribution and use in source and binary forms, with or without
|
|
5
|
+
* modification, are permitted provided that the following conditions
|
|
6
|
+
* are met:
|
|
7
|
+
* 1. Redistributions of source code must retain the above copyright
|
|
8
|
+
* notice, this list of conditions and the following disclaimer.
|
|
9
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
|
10
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
11
|
+
* documentation and/or other materials provided with the distribution.
|
|
12
|
+
* 3. Neither the name of The Omni Group ("Omni") nor the names of
|
|
13
|
+
* its contributors may be used to endorse or promote products derived
|
|
14
|
+
* from this software without specific prior written permission.
|
|
15
|
+
*
|
|
16
|
+
* THIS SOFTWARE IS PROVIDED BY OMNI AND ITS CONTRIBUTORS "AS IS" AND
|
|
17
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL OMNI OR ITS CONTRIBUTORS BE LIABLE FOR
|
|
20
|
+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
22
|
+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
23
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
24
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
25
|
+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
#include "text.h"
|
|
30
|
+
|
|
31
|
+
#include "font.h"
|
|
32
|
+
#include "color.h"
|
|
33
|
+
|
|
34
|
+
#import <Cocoa/Cocoa.h>
|
|
35
|
+
|
|
36
|
+
VALUE text_class;
|
|
37
|
+
|
|
38
|
+
static void _text_clear(Text *text)
|
|
39
|
+
{
|
|
40
|
+
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
41
|
+
@try {
|
|
42
|
+
[text->storage removeLayoutManager:text->layoutManager];
|
|
43
|
+
[text->storage release];
|
|
44
|
+
text->storage = nil;
|
|
45
|
+
|
|
46
|
+
[text->container release];
|
|
47
|
+
text->container = nil;
|
|
48
|
+
|
|
49
|
+
[text->layoutManager release];
|
|
50
|
+
text->layoutManager = nil;
|
|
51
|
+
} @catch (NSException *exc) {
|
|
52
|
+
NSLog(@"%s -- %@", __PRETTY_FUNCTION__, exc);
|
|
53
|
+
} @finally {
|
|
54
|
+
[pool release];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Text *text_get(VALUE self)
|
|
59
|
+
{
|
|
60
|
+
// TODO: Subclassability?
|
|
61
|
+
if (CLASS_OF(self) != text_class)
|
|
62
|
+
rb_raise(rb_eArgError, "Argument must be a Text");
|
|
63
|
+
|
|
64
|
+
Text *text;
|
|
65
|
+
Data_Get_Struct(self, Text, text);
|
|
66
|
+
|
|
67
|
+
if (!text)
|
|
68
|
+
rb_raise(rb_eArgError, "No data specified for Text!");
|
|
69
|
+
|
|
70
|
+
return text;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static void _text_free(Text *text)
|
|
74
|
+
{
|
|
75
|
+
if (text) {
|
|
76
|
+
_text_clear(text);
|
|
77
|
+
free(text);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static VALUE _text_alloc(VALUE klass)
|
|
82
|
+
{
|
|
83
|
+
Text *text = calloc(1, sizeof(*text));
|
|
84
|
+
return Data_Wrap_Struct(klass, NULL, _text_free, text);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
VALUE text_initialize(VALUE self, VALUE string, VALUE font, VALUE color, VALUE alignment)
|
|
88
|
+
{
|
|
89
|
+
Text *text = text_get(self);
|
|
90
|
+
NSFont *f = NIL_P(font) ? nil : font_get(font);
|
|
91
|
+
CGColorRef c = NIL_P(color) ? nil : color_get(color);
|
|
92
|
+
CFStringRef str = _copy_string_for_str(string);
|
|
93
|
+
NSTextAlignment align = NUM2UINT(alignment);
|
|
94
|
+
|
|
95
|
+
// Should already be clear...
|
|
96
|
+
_text_clear(text);
|
|
97
|
+
|
|
98
|
+
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
99
|
+
@try {
|
|
100
|
+
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
|
|
101
|
+
if (f)
|
|
102
|
+
[attributes setObject:f forKey:NSFontAttributeName];
|
|
103
|
+
if (c) {
|
|
104
|
+
NSColor *wrapper = color_create_wrapper(c);
|
|
105
|
+
[attributes setObject:wrapper forKey:NSForegroundColorAttributeName]; // TODO: Are CGColorRef and NSColor bridged?
|
|
106
|
+
[wrapper release];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
NSMutableParagraphStyle *pgStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
|
110
|
+
[pgStyle setAlignment:align];
|
|
111
|
+
[attributes setObject:pgStyle forKey:NSParagraphStyleAttributeName];
|
|
112
|
+
[pgStyle release];
|
|
113
|
+
|
|
114
|
+
text->layoutManager = [[NSLayoutManager alloc] init];
|
|
115
|
+
[text->layoutManager setBackgroundLayoutEnabled:NO];
|
|
116
|
+
|
|
117
|
+
text->container = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(1e9, 1e9)]; // TODO: Warn if you try to draw with an 'infinite' width using center or right alignment?
|
|
118
|
+
[text->container setLineFragmentPadding:0.0f];
|
|
119
|
+
|
|
120
|
+
[text->layoutManager addTextContainer:text->container];
|
|
121
|
+
|
|
122
|
+
text->storage = [[NSTextStorage alloc] initWithString:(NSString *)str attributes:attributes];
|
|
123
|
+
[attributes release];
|
|
124
|
+
|
|
125
|
+
[text->storage addLayoutManager:text->layoutManager];
|
|
126
|
+
|
|
127
|
+
return self;
|
|
128
|
+
} @catch (NSException *exc) {
|
|
129
|
+
NSLog(@"%s -- %@", __PRETTY_FUNCTION__, exc);
|
|
130
|
+
} @finally {
|
|
131
|
+
[pool release];
|
|
132
|
+
}
|
|
133
|
+
rb_raise(rb_eArgError, "Unable to create Text");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static VALUE text_layout_width(VALUE self)
|
|
137
|
+
{
|
|
138
|
+
Text *text = text_get(self);
|
|
139
|
+
return rb_float_new([text->container containerSize].width);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static VALUE text_set_layout_width(VALUE self, VALUE width)
|
|
143
|
+
{
|
|
144
|
+
Text *text = text_get(self);
|
|
145
|
+
NSSize size = [text->container containerSize];
|
|
146
|
+
size.width = NUM2DBL(width);
|
|
147
|
+
[text->container setContainerSize:size];
|
|
148
|
+
return self;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static VALUE text_height_used(VALUE self)
|
|
152
|
+
{
|
|
153
|
+
Text *text = text_get(self);
|
|
154
|
+
|
|
155
|
+
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
156
|
+
@try {
|
|
157
|
+
NSLayoutManager *layoutManager = text->layoutManager;
|
|
158
|
+
|
|
159
|
+
float totalHeight = 0.0;
|
|
160
|
+
unsigned int glyphCount = [layoutManager numberOfGlyphs];
|
|
161
|
+
if (glyphCount > 0) {
|
|
162
|
+
// forces layout
|
|
163
|
+
[layoutManager lineFragmentRectForGlyphAtIndex:glyphCount-1 effectiveRange:NULL];
|
|
164
|
+
|
|
165
|
+
NSTextContainer *textContainer;
|
|
166
|
+
NSArray *textContainers = [layoutManager textContainers];
|
|
167
|
+
unsigned int tcIndex, tcCount = [textContainers count];
|
|
168
|
+
for (tcIndex = 0; tcIndex < tcCount - 1; tcIndex++) {
|
|
169
|
+
textContainer = [textContainers objectAtIndex:tcIndex];
|
|
170
|
+
NSSize containerSize = [textContainer containerSize];
|
|
171
|
+
totalHeight += containerSize.height;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
textContainer = [textContainers lastObject];
|
|
175
|
+
NSRect usedRect = [layoutManager usedRectForTextContainer:textContainer];
|
|
176
|
+
totalHeight += usedRect.size.height;
|
|
177
|
+
}
|
|
178
|
+
return rb_float_new(totalHeight);
|
|
179
|
+
} @catch (NSException *exc) {
|
|
180
|
+
NSLog(@"%s -- %@", __PRETTY_FUNCTION__, exc);
|
|
181
|
+
} @finally {
|
|
182
|
+
[pool release];
|
|
183
|
+
}
|
|
184
|
+
rb_raise(rb_eException, "Unable to determine text height");
|
|
185
|
+
return self;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
static VALUE text_width_used(VALUE self)
|
|
189
|
+
{
|
|
190
|
+
Text *text = text_get(self);
|
|
191
|
+
NSTextStorage *textStorage = text->storage;
|
|
192
|
+
NSLayoutManager *layoutManager = text->layoutManager;
|
|
193
|
+
|
|
194
|
+
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
195
|
+
@try {
|
|
196
|
+
float maximumLineLength = 0.0f;
|
|
197
|
+
unsigned int characterCount = [textStorage length];
|
|
198
|
+
if (characterCount > 0) {
|
|
199
|
+
NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:(NSRange){0, characterCount} actualCharacterRange:NULL];
|
|
200
|
+
if (glyphRange.length > 0) {
|
|
201
|
+
unsigned int glyphLocation = glyphRange.location;
|
|
202
|
+
unsigned int glyphEnd = glyphRange.location + glyphRange.length;
|
|
203
|
+
|
|
204
|
+
while (glyphLocation < glyphEnd) {
|
|
205
|
+
// The line fragment rect isn't what we want (if text is right aligned, it will span the width of the line from the left edge of the text container). We want the glyph bounds...
|
|
206
|
+
NSRange lineGlyphRange;
|
|
207
|
+
[layoutManager lineFragmentRectForGlyphAtIndex:glyphLocation effectiveRange:&lineGlyphRange];
|
|
208
|
+
|
|
209
|
+
// Look at the last character of the given line. If it is a line breaking character, don't include it in the measurements. Otherwise, the glyph bounds will extend to the end of the text container.
|
|
210
|
+
NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineGlyphRange actualGlyphRange:NULL];
|
|
211
|
+
NSRange clippedGlyphRange = lineGlyphRange;
|
|
212
|
+
if (lineCharRange.length) {
|
|
213
|
+
unichar c = [[textStorage string] characterAtIndex:lineCharRange.location + lineCharRange.length - 1];
|
|
214
|
+
if (c == '\n' || c == '\r') { // Other Unicode newline characters?
|
|
215
|
+
// Shorten the character range and get the new glyph range
|
|
216
|
+
lineCharRange.length--;
|
|
217
|
+
clippedGlyphRange = [layoutManager glyphRangeForCharacterRange:lineCharRange actualCharacterRange:NULL];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (!clippedGlyphRange.length) {
|
|
221
|
+
// Only a newline in this line; still need the update to glyphLocation below, though or we hang as in #20274
|
|
222
|
+
} else {
|
|
223
|
+
NSTextContainer *container = [layoutManager textContainerForGlyphAtIndex:glyphLocation effectiveRange:NULL];
|
|
224
|
+
|
|
225
|
+
NSRect glyphBounds = [layoutManager boundingRectForGlyphRange:clippedGlyphRange inTextContainer:container];
|
|
226
|
+
|
|
227
|
+
//NSLog(@"glyphRange = %@, lineFrag = %@, glyphBounds = %@", NSStringFromRange(clippedGlyphRange), NSStringFromRect(lineFrag), NSStringFromRect(glyphBounds));
|
|
228
|
+
|
|
229
|
+
maximumLineLength = MAX(glyphBounds.size.width, maximumLineLength);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Step by the unclipped glyph range or we'll go into an infinite loop when we chop off a newline
|
|
233
|
+
glyphLocation = lineGlyphRange.location + lineGlyphRange.length;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return rb_float_new(maximumLineLength);
|
|
239
|
+
} @catch (NSException *exc) {
|
|
240
|
+
NSLog(@"%s -- %@", __PRETTY_FUNCTION__, exc);
|
|
241
|
+
} @finally {
|
|
242
|
+
[pool release];
|
|
243
|
+
}
|
|
244
|
+
rb_raise(rb_eException, "Unable to determine text width");
|
|
245
|
+
return self;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
void Init_text(void)
|
|
249
|
+
{
|
|
250
|
+
text_class = rb_define_class_under(module, "Text", rb_cObject);
|
|
251
|
+
rb_define_alloc_func(text_class, _text_alloc);
|
|
252
|
+
|
|
253
|
+
rb_define_private_method(text_class, "_initialize", text_initialize, 4);
|
|
254
|
+
rb_define_method(text_class, "layout_width", text_layout_width, 0);
|
|
255
|
+
rb_define_method(text_class, "layout_width=", text_set_layout_width, 1);
|
|
256
|
+
rb_define_method(text_class, "height_used", text_height_used, 0);
|
|
257
|
+
rb_define_method(text_class, "width_used", text_width_used, 0);
|
|
258
|
+
}
|
data/ext/text.h
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2006, The Omni Group, Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
OPEN PERMISSION TO USE AND REPRODUCE OMNI SOURCE CODE SOFTWARE
|
|
5
|
+
|
|
6
|
+
Omni Source Code software is available from The Omni Group on their web
|
|
7
|
+
site at www.omnigroup.com.
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
10
|
+
copy of this software and associated documentation files (the "Software"),
|
|
11
|
+
to deal in the Software without restriction, including without limitation
|
|
12
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
13
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
|
14
|
+
Software is furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
Any original copyright notices and this permission notice shall be included
|
|
17
|
+
in all copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
24
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
25
|
+
DEALINGS IN THE SOFTWARE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@class NSTextStorage, NSTextContainer, NSLayoutManager;
|
|
30
|
+
|
|
31
|
+
typedef struct {
|
|
32
|
+
NSTextStorage *storage;
|
|
33
|
+
NSTextContainer *container;
|
|
34
|
+
NSLayoutManager *layoutManager;
|
|
35
|
+
} Text;
|
|
36
|
+
|
|
37
|
+
extern void Init_text(void);
|
|
38
|
+
extern VALUE text_class;
|
|
39
|
+
extern Text *text_get(VALUE self);
|
data/ext/utilities.c
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2006, The Omni Group, Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
OPEN PERMISSION TO USE AND REPRODUCE OMNI SOURCE CODE SOFTWARE
|
|
5
|
+
|
|
6
|
+
Omni Source Code software is available from The Omni Group on their web
|
|
7
|
+
site at www.omnigroup.com.
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
10
|
+
copy of this software and associated documentation files (the "Software"),
|
|
11
|
+
to deal in the Software without restriction, including without limitation
|
|
12
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
13
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
|
14
|
+
Software is furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
Any original copyright notices and this permission notice shall be included
|
|
17
|
+
in all copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
24
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
25
|
+
DEALINGS IN THE SOFTWARE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#include "utilities.h"
|
|
30
|
+
|
|
31
|
+
#import <Foundation/NSObject.h>
|
|
32
|
+
#import <objc/objc-runtime.h>
|
|
33
|
+
|
|
34
|
+
VALUE _symbol_for_string(CFStringRef str)
|
|
35
|
+
{
|
|
36
|
+
CFDataRef utf8 = CFStringCreateExternalRepresentation(kCFAllocatorDefault, str, kCFStringEncodingUTF8, '?');
|
|
37
|
+
CFIndex utf8Length = CFDataGetLength(utf8);
|
|
38
|
+
char *utf8Bytes = malloc(utf8Length + 1);
|
|
39
|
+
memcpy(utf8Bytes, CFDataGetBytePtr(utf8) , utf8Length);
|
|
40
|
+
utf8Bytes[utf8Length] = '\0';
|
|
41
|
+
CFRelease(utf8);
|
|
42
|
+
|
|
43
|
+
VALUE symbol = ID2SYM(rb_intern(utf8Bytes));
|
|
44
|
+
free(utf8Bytes);
|
|
45
|
+
|
|
46
|
+
return symbol;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
CFStringRef _copy_string_for_str(VALUE str)
|
|
50
|
+
{
|
|
51
|
+
// TODO: Unicode?
|
|
52
|
+
const unsigned char *s = (const unsigned char *)StringValueCStr(str);
|
|
53
|
+
return CFStringCreateWithBytes(kCFAllocatorDefault, s, strlen((const char *)s), kCFStringEncodingUTF8, false/*isExternalRepresentation*/);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
VALUE _value_for_string(CFStringRef str)
|
|
57
|
+
{
|
|
58
|
+
// TODO: Unicode?
|
|
59
|
+
// TODO: Check for conversion failure and raise
|
|
60
|
+
CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, str, kCFStringEncodingUTF8, 0);
|
|
61
|
+
|
|
62
|
+
VALUE value = rb_str_new((char *)CFDataGetBytePtr(data), CFDataGetLength(data));
|
|
63
|
+
|
|
64
|
+
if (data)
|
|
65
|
+
CFRelease(data);
|
|
66
|
+
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
CFURLRef _create_url_for_path(VALUE path)
|
|
71
|
+
{
|
|
72
|
+
const char *pathCString = StringValueCStr(path); // checks type
|
|
73
|
+
|
|
74
|
+
// TODO: Test non-ASCII path
|
|
75
|
+
CFStringRef pathString = CFStringCreateWithCString(kCFAllocatorDefault, pathCString, kCFStringEncodingUTF8);
|
|
76
|
+
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathString, kCFURLPOSIXPathStyle, false/*isDirectory*/);
|
|
77
|
+
CFRelease(pathString);
|
|
78
|
+
|
|
79
|
+
if (!url)
|
|
80
|
+
rb_raise(rb_eException, "Unable to create URL from given path");
|
|
81
|
+
return url;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
CFDataRef _create_data_from_string(VALUE value, Boolean copy)
|
|
85
|
+
{
|
|
86
|
+
value = rb_str_to_str(value);
|
|
87
|
+
struct RString *str = RSTRING(value);
|
|
88
|
+
|
|
89
|
+
if (copy)
|
|
90
|
+
return CFDataCreate(kCFAllocatorDefault, (const UInt8 *)str->ptr, str->len);
|
|
91
|
+
else
|
|
92
|
+
return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)str->ptr, str->len, NULL);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Common alloc/free handlers for CFType-wrapping classes
|
|
96
|
+
static void _cftype_free(CFTypeRef *cf)
|
|
97
|
+
{
|
|
98
|
+
if (cf) {
|
|
99
|
+
if (*cf)
|
|
100
|
+
CFRelease(*cf);
|
|
101
|
+
free(cf);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
VALUE _cftype_alloc(VALUE klass)
|
|
106
|
+
{
|
|
107
|
+
CFTypeRef *cf = calloc(1, sizeof(*cf));
|
|
108
|
+
return Data_Wrap_Struct(klass, NULL, _cftype_free, cf);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Doesn't allow NULL to be returned since callers depend on this for ease of use
|
|
112
|
+
CFTypeRef _cftype_get(VALUE self, CFTypeID typeID, const char *typeName)
|
|
113
|
+
{
|
|
114
|
+
CFTypeRef *cf;
|
|
115
|
+
Data_Get_Struct(self, CFTypeRef, cf);
|
|
116
|
+
|
|
117
|
+
if (!cf)
|
|
118
|
+
rb_raise(rb_eArgError, "No data specified for CFType holder!");
|
|
119
|
+
|
|
120
|
+
CFTypeRef result = *cf;
|
|
121
|
+
if (!result)
|
|
122
|
+
rb_raise(rb_eArgError, "No %s wrapped in %s", typeName, rb_obj_classname(self));
|
|
123
|
+
|
|
124
|
+
if (CFGetTypeID(result) != typeID)
|
|
125
|
+
rb_raise(rb_eArgError, "%s is wrapping something other than a %s, type id is %d but should be %d", rb_obj_classname(self), typeName, CFGetTypeID(result), typeID);
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void _cftype_set(VALUE self, CFTypeID typeID, const char *typeName, CFTypeRef value)
|
|
130
|
+
{
|
|
131
|
+
if (value && (CFGetTypeID(value) != typeID))
|
|
132
|
+
rb_raise(rb_eArgError, "Attempt to set a non-%s value on a %s, type id is %d but should be %d", typeName, rb_obj_classname(self), CFGetTypeID(value), typeID);
|
|
133
|
+
|
|
134
|
+
CFTypeRef *cf;
|
|
135
|
+
Data_Get_Struct(self, CFTypeRef, cf);
|
|
136
|
+
|
|
137
|
+
if (!cf)
|
|
138
|
+
rb_raise(rb_eArgError, "No data specified for CFType holder!");
|
|
139
|
+
|
|
140
|
+
if (*cf)
|
|
141
|
+
CFRelease(*cf);
|
|
142
|
+
*cf = value;
|
|
143
|
+
if (value)
|
|
144
|
+
CFRetain(value);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Doesn't allow nil to be returned since callers depend on this for ease of use
|
|
148
|
+
id _objc_type_get(VALUE self, Class cls)
|
|
149
|
+
{
|
|
150
|
+
id *obj;
|
|
151
|
+
Data_Get_Struct(self, id, obj);
|
|
152
|
+
|
|
153
|
+
if (!obj)
|
|
154
|
+
rb_raise(rb_eArgError, "No data specified for object holder!");
|
|
155
|
+
|
|
156
|
+
id result = *obj;
|
|
157
|
+
if (!result)
|
|
158
|
+
rb_raise(rb_eArgError, "No %s wrapped in %s", cls->name, rb_obj_classname(self));
|
|
159
|
+
|
|
160
|
+
if (![result isKindOfClass:cls])
|
|
161
|
+
rb_raise(rb_eArgError, "%s is wrapping something other than a %s", rb_obj_classname(self), cls->name);
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
void _objc_type_set(VALUE self, Class cls, id value)
|
|
166
|
+
{
|
|
167
|
+
if (value && ![value isKindOfClass:cls])
|
|
168
|
+
rb_raise(rb_eArgError, "Attempt to set a non-%s value on a %s", cls->name, rb_obj_classname(self));
|
|
169
|
+
|
|
170
|
+
id *obj;
|
|
171
|
+
Data_Get_Struct(self, id, obj);
|
|
172
|
+
|
|
173
|
+
if (!obj)
|
|
174
|
+
rb_raise(rb_eArgError, "No data specified for object holder!");
|
|
175
|
+
|
|
176
|
+
if (*obj)
|
|
177
|
+
[*obj release];
|
|
178
|
+
*obj = value;
|
|
179
|
+
if (value)
|
|
180
|
+
[value retain];
|
|
181
|
+
}
|