Skip to content

Commit cd3b67e

Browse files
committed
Added CSV badge. Minor impromevents. Thinking about drawing to a PDF context instead of a bitmap.
1 parent 64ef13e commit cd3b67e

File tree

3 files changed

+163
-553
lines changed

3 files changed

+163
-553
lines changed

GenerateThumbnailForURL.m

Lines changed: 123 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
#import "CSVDocument.h"
66
#import "CSVRowObject.h"
77

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"
912

1013
static CGContextRef createRGBABitmapContext(CGSize pixelSize);
14+
//static CGContextRef createVectorContext(CGSize pixelSize)
1115

1216

1317
/* -----------------------------------------------------------------------------
@@ -46,22 +50,24 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
4650

4751
// Parse the data if still interested in the thumbnail
4852
if(false == QLThumbnailRequestIsCancelled(thumbnail)) {
49-
CGFloat thumbnailSize = 256.0;
5053
CGFloat fontSize = 12.0;
5154
CGFloat rowHeight = 18.0;
52-
NSUInteger numRows = ceilf(thumbnailSize / rowHeight);
55+
NSUInteger numRows = ceilf(THUMB_SIZE / rowHeight);
5356

5457
CSVDocument *csvDoc = [CSVDocument csvDoc];
5558
NSUInteger gotRows = [csvDoc numRowsFromCSVString:fileString maxRows:numRows error:NULL];
5659

5760

5861
// Draw an icon if still interested in the thumbnail
5962
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;
6266

6367
CGContextRef context = createRGBABitmapContext(maxBounds.size);
68+
//CGContextRef context = createVectorContext(maxBounds.size);
6469
if(context) {
70+
CGPDFContextBeginPage(context, NULL);
6571

6672
// Flip CoreGraphics coordinate system
6773
CGContextScaleCTM(context, 1.0, -1.0);
@@ -75,9 +81,9 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
7581
CGFloat borderWidth = 1.0;
7682

7783
// 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];
7985
[NSGraphicsContext setCurrentContext:nsContext];
80-
if(nsContext) {
86+
if(nil != nsContext) {
8187
NSFont *myFont = [NSFont systemFontOfSize:fontSize];
8288
NSColor *blackColor = [NSColor blackColor];
8389
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:myFont, NSFontAttributeName,
@@ -95,18 +101,22 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
95101

96102
CGRect rowRect = CGRectMake(cellX, 0.0, maxBounds.size.width - cellX, rowHeight);
97103
maxCellStringWidth = 0.0;
98-
BOOL altRow = NO;
99104
BOOL isFirstColumn = [csvDoc isFirstColumn:colKey];
105+
BOOL altRow = NO;
100106

101107
// loop rows
102108
for(CSVRowObject *row in csvDoc.rows) {
103109

110+
111+
// *****
104112
// Draw background
105113
if(isFirstColumn) {
106114
CGContextSetFillColorWithColor(context, altRow ? altRowBG : rowBG);
107115
CGContextFillRect(context, rowRect);
108116
}
109117

118+
119+
// *****
110120
// Draw border
111121
else {
112122
CGContextMoveToPoint(context, cellX + borderWidth / 2, rowRect.origin.y);
@@ -115,6 +125,8 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
115125
CGContextStrokePath(context);
116126
}
117127

128+
129+
// *****
118130
// Draw text
119131
NSRect textRect = NSRectFromCGRect(rowRect);
120132
textRect.size.width -= 2 * textXPadding;
@@ -128,43 +140,94 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
128140
}
129141
altRow = !altRow;
130142
rowRect.origin.y += rowHeight;
143+
144+
// adjust usedBounds
145+
if(usedBounds.size.height < rowRect.origin.y) {
146+
usedBounds.size.height = rowRect.origin.y;
147+
}
131148
}
132149

133150
cellX += maxCellStringWidth + 2 * textXPadding;
151+
usedBounds.size.width = cellX;
134152
}
135153

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+
}
144176
}
145177
}
146178

147179
CGColorRelease(borderColor);
148180
CGColorRelease(rowBG);
149181
CGColorRelease(altRowBG);
150182

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);
154185
CGImageRef fullImage = CGBitmapContextCreateImage(context);
155186
CGImageRef usedImage = CGImageCreateWithImageInRect(fullImage, usedBounds);
156187
CGImageRelease(fullImage);
188+
189+
// Draw the image to the thumbnail request
190+
CGContextRef thumbContext = QLThumbnailRequestCreateContext(thumbnail, usedBounds.size, false, NULL);
157191
CGContextDrawImage(thumbContext, usedBounds, usedImage);
158192
CGImageRelease(usedImage);
159193

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);
164198
}
165-
CFRelease(context);
166199

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);
167229
QLThumbnailRequestFlushContext(thumbnail, thumbContext);
230+
CGContextRelease(thumbContext);
168231
}
169232
}
170233
}
@@ -211,3 +274,39 @@ static CGContextRef createRGBABitmapContext(CGSize pixelSize)
211274
return context;
212275
}
213276

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

Comments
 (0)