5
5
#import " CSVDocument.h"
6
6
#import " CSVRowObject.h"
7
7
8
- #define MIN_WIDTH 40.0
8
+ #define THUMB_SIZE 256.0
9
+ #define MIN_WIDTH 0.375 // fraction of height
10
+ #define MIN_HEIGHT 0.375 // fraction of width
11
+ #define BADGE @" CSV"
9
12
10
13
static CGContextRef createRGBABitmapContext (CGSize pixelSize);
14
+ // static CGContextRef createVectorContext(CGSize pixelSize)
11
15
12
16
13
17
/* -----------------------------------------------------------------------------
@@ -46,22 +50,24 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
46
50
47
51
// Parse the data if still interested in the thumbnail
48
52
if (false == QLThumbnailRequestIsCancelled (thumbnail)) {
49
- CGFloat thumbnailSize = 256.0 ;
50
53
CGFloat fontSize = 12.0 ;
51
54
CGFloat rowHeight = 18.0 ;
52
- NSUInteger numRows = ceilf (thumbnailSize / rowHeight);
55
+ NSUInteger numRows = ceilf (THUMB_SIZE / rowHeight);
53
56
54
57
CSVDocument *csvDoc = [CSVDocument csvDoc ];
55
58
NSUInteger gotRows = [csvDoc numRowsFromCSVString: fileString maxRows: numRows error: NULL ];
56
59
57
60
58
61
// Draw an icon if still interested in the thumbnail
59
62
if ((gotRows > 0 ) && (false == QLThumbnailRequestIsCancelled (thumbnail))) {
60
- CGRect maxBounds = CGRectMake (0.0 , 0.0 , thumbnailSize, thumbnailSize);
61
- CGRect usedBounds = CGRectMake (0.0 , 0.0 , thumbnailSize, thumbnailSize);
63
+ CGRect maxBounds = CGRectMake (0.0 , 0.0 , THUMB_SIZE, THUMB_SIZE);
64
+ CGRect usedBounds = CGRectMake (0.0 , 0.0 , 0.0 , 0.0 );
65
+ CGFloat badgeReferenceSize = THUMB_SIZE;
62
66
63
67
CGContextRef context = createRGBABitmapContext (maxBounds.size );
68
+ // CGContextRef context = createVectorContext(maxBounds.size);
64
69
if (context) {
70
+ CGPDFContextBeginPage (context, NULL );
65
71
66
72
// Flip CoreGraphics coordinate system
67
73
CGContextScaleCTM (context, 1.0 , -1.0 );
@@ -75,9 +81,9 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
75
81
CGFloat borderWidth = 1.0 ;
76
82
77
83
// We use NSGraphicsContext for the strings due to easier string drawing :P
78
- NSGraphicsContext * nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort: (void *)context flipped: YES ];
84
+ NSGraphicsContext * nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort: (void *)context flipped: YES ];
79
85
[NSGraphicsContext setCurrentContext: nsContext];
80
- if (nsContext) {
86
+ if (nil != nsContext) {
81
87
NSFont *myFont = [NSFont systemFontOfSize: fontSize];
82
88
NSColor *blackColor = [NSColor blackColor ];
83
89
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys: myFont, NSFontAttributeName ,
@@ -95,18 +101,22 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
95
101
96
102
CGRect rowRect = CGRectMake (cellX, 0.0 , maxBounds.size .width - cellX, rowHeight);
97
103
maxCellStringWidth = 0.0 ;
98
- BOOL altRow = NO ;
99
104
BOOL isFirstColumn = [csvDoc isFirstColumn: colKey];
105
+ BOOL altRow = NO ;
100
106
101
107
// loop rows
102
108
for (CSVRowObject *row in csvDoc.rows ) {
103
109
110
+
111
+ // *****
104
112
// Draw background
105
113
if (isFirstColumn) {
106
114
CGContextSetFillColorWithColor (context, altRow ? altRowBG : rowBG);
107
115
CGContextFillRect (context, rowRect);
108
116
}
109
117
118
+
119
+ // *****
110
120
// Draw border
111
121
else {
112
122
CGContextMoveToPoint (context, cellX + borderWidth / 2 , rowRect.origin .y );
@@ -115,6 +125,8 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
115
125
CGContextStrokePath (context);
116
126
}
117
127
128
+
129
+ // *****
118
130
// Draw text
119
131
NSRect textRect = NSRectFromCGRect (rowRect);
120
132
textRect.size .width -= 2 * textXPadding;
@@ -128,43 +140,94 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
128
140
}
129
141
altRow = !altRow;
130
142
rowRect.origin .y += rowHeight;
143
+
144
+ // adjust usedBounds
145
+ if (usedBounds.size .height < rowRect.origin .y ) {
146
+ usedBounds.size .height = rowRect.origin .y ;
147
+ }
131
148
}
132
149
133
150
cellX += maxCellStringWidth + 2 * textXPadding;
151
+ usedBounds.size .width = cellX;
134
152
}
135
153
136
- // adjust usedBounds.size.width...
137
- if (cellX < maxBounds.size .width ) {
138
- usedBounds.size .width = (cellX < MIN_WIDTH) ? MIN_WIDTH : cellX;
139
- }
140
-
141
- // ...and usedBounds.size.height
142
- if (gotRows < numRows) {
143
- usedBounds.size .height = gotRows * rowHeight;
154
+ // scale usedBounds to fill at least either width or height
155
+ if (usedBounds.size .width < THUMB_SIZE || usedBounds.size .height < THUMB_SIZE) {
156
+
157
+ if (usedBounds.size .width < usedBounds.size .height ) {
158
+ badgeReferenceSize = usedBounds.size .height ;
159
+
160
+ CGFloat minWidth = usedBounds.size .height * MIN_WIDTH;
161
+ usedBounds.size .width = (usedBounds.size .width < minWidth) ? minWidth : usedBounds.size .width ;
162
+ }
163
+
164
+ else {
165
+ badgeReferenceSize = usedBounds.size .width ;
166
+
167
+ CGFloat minHeight = usedBounds.size .width * MIN_HEIGHT;
168
+ if (usedBounds.size .height < minHeight) {
169
+ CGRect missingRect = CGRectMake (0.0 , usedBounds.size .height , usedBounds.size .width , (minHeight - usedBounds.size .height ));
170
+ CGContextSetFillColorWithColor (context, rowBG);
171
+ CGContextFillRect (context, missingRect);
172
+
173
+ usedBounds.size .height = minHeight;
174
+ }
175
+ }
144
176
}
145
177
}
146
178
147
179
CGColorRelease (borderColor);
148
180
CGColorRelease (rowBG);
149
181
CGColorRelease (altRowBG);
150
182
151
- // Draw the image to the thumbnail request
152
- CGContextRef thumbContext = QLThumbnailRequestCreateContext (thumbnail, usedBounds.size , false , NULL );
153
-
183
+ // Create a CGImage
184
+ CGPDFContextEndPage (context);
154
185
CGImageRef fullImage = CGBitmapContextCreateImage (context);
155
186
CGImageRef usedImage = CGImageCreateWithImageInRect (fullImage, usedBounds);
156
187
CGImageRelease (fullImage);
188
+
189
+ // Draw the image to the thumbnail request
190
+ CGContextRef thumbContext = QLThumbnailRequestCreateContext (thumbnail, usedBounds.size , false , NULL );
157
191
CGContextDrawImage (thumbContext, usedBounds, usedImage);
158
192
CGImageRelease (usedImage);
159
193
160
- // we no longer need the bitmap data; free
161
- char *bitmapData = CGBitmapContextGetData (context);
162
- if (bitmapData ) {
163
- free (bitmapData );
194
+ // we no longer need the bitmap data; free (malloc'ed by createRGBABitmapContext() )
195
+ char *contextData = CGBitmapContextGetData (context);
196
+ if (contextData ) {
197
+ free (contextData );
164
198
}
165
- CFRelease (context);
166
199
200
+
201
+ // *****
202
+ // Draw the CSV badge to the icon
203
+ NSGraphicsContext *thumbNsContext = [NSGraphicsContext graphicsContextWithGraphicsPort: (void *)thumbContext flipped: NO ];
204
+ [NSGraphicsContext setCurrentContext: thumbNsContext];
205
+ if (nil != thumbNsContext) {
206
+ NSString *badgeString = BADGE;
207
+ CGFloat badgeFontSize = badgeReferenceSize * 0.15 ;
208
+ NSFont *badgeFont = [NSFont boldSystemFontOfSize: badgeFontSize];
209
+ NSColor *badgeColor = [NSColor darkGrayColor ];
210
+ NSShadow *badgeShadow = [[[NSShadow alloc ] init ] autorelease ];
211
+ [badgeShadow setShadowOffset: NSMakeSize (0.0 , 0.0 )];
212
+ [badgeShadow setShadowBlurRadius: badgeFontSize * 0.1 ];
213
+ [badgeShadow setShadowColor: [NSColor whiteColor ]];
214
+
215
+ // Set attributes and draw
216
+ NSDictionary *badgeAttributes = [NSDictionary dictionaryWithObjectsAndKeys: badgeFont, NSFontAttributeName ,
217
+ badgeColor, NSForegroundColorAttributeName ,
218
+ badgeShadow, NSShadowAttributeName , nil ];
219
+ NSSize badgeSize = [badgeString sizeWithAttributes: badgeAttributes];
220
+ NSRect badgeRect = NSMakeRect ((usedBounds.size .width / 2 ) - (badgeSize.width / 2 ), 0.25 * badgeFontSize, 0.0 , 0.0 );
221
+ badgeRect.size = badgeSize;
222
+
223
+ [badgeString drawWithRect: badgeRect options: NSStringDrawingUsesLineFragmentOrigin attributes: badgeAttributes];
224
+ }
225
+
226
+
227
+ // Clean up
228
+ CGContextRelease (context);
167
229
QLThumbnailRequestFlushContext (thumbnail, thumbContext);
230
+ CGContextRelease (thumbContext);
168
231
}
169
232
}
170
233
}
@@ -211,3 +274,39 @@ static CGContextRef createRGBABitmapContext(CGSize pixelSize)
211
274
return context;
212
275
}
213
276
277
+ /*
278
+ static CGContextRef createVectorContext(CGSize pixelSize)
279
+ {
280
+ CGRect mediaBox = CGRectMake(0.0, 0.0, 0.0, 0.0);
281
+ mediaBox.size = pixelSize;
282
+
283
+ // allocate needed bytes
284
+ CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); // unlimited size; hopefully we won't regret this :)
285
+ if(NULL == bitmapData) {
286
+ fprintf(stderr, "Oops, could not allocate mutable data!");
287
+ return NULL;
288
+ }
289
+
290
+ // create the context
291
+ CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData(data);
292
+ CGContextRef context = CGPDFContextCreate(consumer, &mediaBox, NULL);
293
+ CGDataConsumerRelease(consumer);
294
+
295
+ // context creation fail
296
+ if(NULL == context) {
297
+ free(bitmapData);
298
+ fprintf(stderr, "Oops, could not create the context!");
299
+ return NULL;
300
+ }
301
+
302
+ return context;
303
+
304
+
305
+ // Don't forget creating pages
306
+ // CGPDFContextBeginPage(pdfContext, NULL);
307
+ // CGPDFContextEndPage(pdfContext);
308
+
309
+ // and release the data
310
+ // CFRelease(data);
311
+ } // */
312
+
0 commit comments