diff --git a/QLOPML.xcodeproj/project.pbxproj b/QLOPML.xcodeproj/project.pbxproj index 08b8927..b968046 100644 --- a/QLOPML.xcodeproj/project.pbxproj +++ b/QLOPML.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 540A649E22EE78B200470937 /* GeneratePreviewForURL.c in Sources */ = {isa = PBXBuildFile; fileRef = 540A649D22EE78B200470937 /* GeneratePreviewForURL.c */; }; 540A64A022EE78B200470937 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 540A649F22EE78B200470937 /* main.c */; }; 541EF8B322EEFBEA00C415AA /* style.css in Resources */ = {isa = PBXBuildFile; fileRef = 541EF8B122EEFB2300C415AA /* style.css */; }; + 54A75F6023D1269200754813 /* style-thumb.css in Resources */ = {isa = PBXBuildFile; fileRef = 54A75F5F23D1269100754813 /* style-thumb.css */; }; 54BFFC1123D09E7300012FBB /* opml-lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 54BFFC0F23D09E7300012FBB /* opml-lib.h */; }; 54BFFC1223D09E7300012FBB /* opml-lib.m in Sources */ = {isa = PBXBuildFile; fileRef = 54BFFC1023D09E7300012FBB /* opml-lib.m */; }; /* End PBXBuildFile section */ @@ -22,7 +23,8 @@ 540A649F22EE78B200470937 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 540A64A122EE78B200470937 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 541EF8B122EEFB2300C415AA /* style.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = style.css; sourceTree = ""; }; - 54BFFC0423D0988A00012FBB /* sample.opml */ = {isa = PBXFileReference; lastKnownFileType = file; path = sample.opml; sourceTree = SOURCE_ROOT; }; + 54A75F5F23D1269100754813 /* style-thumb.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = "style-thumb.css"; sourceTree = ""; }; + 54BFFC0423D0988A00012FBB /* sample.opml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = sample.opml; sourceTree = SOURCE_ROOT; }; 54BFFC0F23D09E7300012FBB /* opml-lib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "opml-lib.h"; sourceTree = ""; }; 54BFFC1023D09E7300012FBB /* opml-lib.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "opml-lib.m"; sourceTree = ""; }; 54FB05D22305C8F400A088AD /* QLOPML.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QLOPML.entitlements; sourceTree = ""; }; @@ -66,6 +68,7 @@ 540A649F22EE78B200470937 /* main.c */, 540A64A122EE78B200470937 /* Info.plist */, 541EF8B122EEFB2300C415AA /* style.css */, + 54A75F5F23D1269100754813 /* style-thumb.css */, 54BFFC0423D0988A00012FBB /* sample.opml */, ); path = QLOPML; @@ -147,6 +150,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 54A75F6023D1269200754813 /* style-thumb.css in Resources */, 541EF8B322EEFBEA00C415AA /* style.css in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/QLOPML/GeneratePreviewForURL.c b/QLOPML/GeneratePreviewForURL.c index 6c6fa02..98618e6 100644 --- a/QLOPML/GeneratePreviewForURL.c +++ b/QLOPML/GeneratePreviewForURL.c @@ -14,9 +14,8 @@ void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview); OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) { - // qlmanage -r && qlmanage -p test.opml -o tmp/ && edit tmp/test.opml.qlpreview/Preview.html CFBundleRef bundle = QLPreviewRequestGetGeneratorBundle(preview); - CFDataRef data = renderOPML(url, bundle); + CFDataRef data = generateHTML(url, bundle, false); if (data) { QLPreviewRequestSetDataRepresentation(preview, data, kUTTypeHTML, NULL); CFRelease(data); diff --git a/QLOPML/GenerateThumbnailForURL.c b/QLOPML/GenerateThumbnailForURL.c index 9a2d4f5..727e6f2 100644 --- a/QLOPML/GenerateThumbnailForURL.c +++ b/QLOPML/GenerateThumbnailForURL.c @@ -1,6 +1,7 @@ #include #include #include +#include "opml-lib.h" OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize); void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail); @@ -13,10 +14,27 @@ void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbn OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize) { - // TODO: generate icon with feed count? -// CGImageRef image = CGImageCreate... -// QLThumbnailRequestSetImage(thumbnail, image, NULL); + // test with: + // rm -rf ~/Library/QuickLook/QLOPML.qlgenerator && qlmanage -r && rsync -a ~/Library/Developer/Xcode/DerivedData/QLOPML-*/Build/Products/Debug/QLOPML.qlgenerator ~/Library/QuickLook/ && qlmanage -t sample.opml -s 512 -i + CFBundleRef bundle = QLThumbnailRequestGetGeneratorBundle(thumbnail); + CFDataRef data = generateHTML(url, bundle, true); + if (data) { + QLThumbnailRequestSetThumbnailWithDataRepresentation(thumbnail, data, kUTTypeHTML, NULL, NULL); + CFRelease(data); + } return noErr; + +// CGSize thumbSize = CGSizeMake(maxSize.width * (600/800.0), maxSize.height); +// // Draw the webview in the correct context +// CGContextRef context = QLThumbnailRequestCreateContext(thumbnail, thumbSize, false, NULL); +// +// if (context) { +// CFBundleRef bundle = QLThumbnailRequestGetGeneratorBundle(thumbnail); +// renderThumbnail(url, bundle, context, maxSize); +// QLThumbnailRequestFlushContext(thumbnail, context); +// CFRelease(context); +// } +// return noErr; } void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail) diff --git a/QLOPML/Info.plist b/QLOPML/Info.plist index 6195123..30ee865 100644 --- a/QLOPML/Info.plist +++ b/QLOPML/Info.plist @@ -25,7 +25,7 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 1.2 + 1.3 CFBundleVersion 1 CFPlugInDynamicRegisterFunction @@ -49,7 +49,7 @@ NSHumanReadableCopyright Copyright © 2019 relikd. Public Domain. QLNeedsToBeRunInMainThread - + QLPreviewHeight 600 QLPreviewWidth diff --git a/QLOPML/opml-lib.h b/QLOPML/opml-lib.h index 207bba5..424462b 100644 --- a/QLOPML/opml-lib.h +++ b/QLOPML/opml-lib.h @@ -1,6 +1,7 @@ #ifndef opml_lib_h #define opml_lib_h -CFDataRef renderOPML(CFURLRef url, CFBundleRef bundle); +CFDataRef generateHTML(CFURLRef url, CFBundleRef bundle, Boolean thumb); +//void renderThumbnail(CFURLRef url, CFBundleRef bundle, CGContextRef context, CGSize maxSize); #endif /* opml_lib_h */ diff --git a/QLOPML/opml-lib.m b/QLOPML/opml-lib.m index a5db3cc..edfc5f7 100644 --- a/QLOPML/opml-lib.m +++ b/QLOPML/opml-lib.m @@ -1,8 +1,6 @@ -//#import -//#import -//#import #import #import +//#import // --------------------------------------------------------------- // | @@ -28,9 +26,11 @@ NSXMLElement* section(NSString *title, NSString *container, NSXMLElement *parent return div; } -void appendNode(NSXMLElement *child, NSXMLElement *parent) { +void appendNode(NSXMLElement *child, NSXMLElement *parent, Boolean thumb) { if ([child.name isEqualToString:@"head"]) { + if (thumb) + return; NSXMLElement *dl = section(@"Metadata:", @"dl", parent); for (NSXMLElement *head in child.children) { make(@"dt", head.name, dl); @@ -40,21 +40,23 @@ void appendNode(NSXMLElement *child, NSXMLElement *parent) { } if ([child.name isEqualToString:@"body"]) { - parent = section(@"Content:", @"ul", parent); + parent = thumb ? make(@"ul", nil, parent) : section(@"Content:", @"ul", parent); } else if ([child.name isEqualToString:@"outline"]) { if ([child attributeForName:@"separator"].stringValue) { make(@"hr", nil, parent); } else { NSString *desc = [child attributeForName:@"title"].stringValue; - NSString *xmlUrl = [child attributeForName:@"xmlUrl"].stringValue; if (!desc || desc.length == 0) desc = [child attributeForName:@"text"].stringValue; // refreshInterval NSXMLElement *li = make(@"li", desc, parent); - if (xmlUrl && xmlUrl.length > 0) { - [li addChild:[NSXMLNode textWithStringValue:@" — "]]; - attribute(make(@"a", xmlUrl, li), @"href", xmlUrl); + if (!thumb) { + NSString *xmlUrl = [child attributeForName:@"xmlUrl"].stringValue; + if (xmlUrl && xmlUrl.length > 0) { + [li addChild:[NSXMLNode textWithStringValue:@" — "]]; + attribute(make(@"a", xmlUrl, li), @"href", xmlUrl); + } } } if (child.childCount > 0) { @@ -62,13 +64,13 @@ void appendNode(NSXMLElement *child, NSXMLElement *parent) { } } for (NSXMLElement *c in child.children) { - appendNode(c, parent); + appendNode(c, parent, thumb); } } -CFDataRef renderOPML(CFURLRef url, CFBundleRef bundle) { +NSData* generateHTMLData(NSURL *url, CFBundleRef bundle, Boolean thumb) { NSError *err; - NSXMLDocument *doc = [[NSXMLDocument alloc] initWithContentsOfURL:(__bridge NSURL*)url options:0 error:&err]; + NSXMLDocument *doc = [[NSXMLDocument alloc] initWithContentsOfURL:url options:0 error:&err]; if (err || !doc) { printf("ERROR: %s\n", err.description.UTF8String); return nil; @@ -78,15 +80,40 @@ CFDataRef renderOPML(CFURLRef url, CFBundleRef bundle) { NSXMLElement *head = make(@"head", nil, html); make(@"title", @"OPML file", head); - CFURLRef path = CFBundleCopyResourceURL(bundle, CFSTR("style"), CFSTR("css"), NULL); + CFURLRef path = CFBundleCopyResourceURL(bundle, thumb ? CFSTR("style-thumb") : CFSTR("style"), CFSTR("css"), NULL); NSString *data = [NSString stringWithContentsOfFile:CFBridgingRelease(path) encoding:NSUTF8StringEncoding error:nil]; make(@"style", data, head); NSXMLElement *body = make(@"body", nil, html); for (NSXMLElement *child in doc.children) { - appendNode(child, body); + appendNode(child, body, thumb); } NSXMLDocument *xml = [NSXMLDocument documentWithRootElement:html]; - return CFBridgingRetain([xml XMLDataWithOptions:NSXMLNodePrettyPrint | NSXMLNodeCompactEmptyElement]); + return [xml XMLDataWithOptions:NSXMLNodePrettyPrint | NSXMLNodeCompactEmptyElement]; } + +CFDataRef generateHTML(CFURLRef url, CFBundleRef bundle, Boolean thumb) { + return CFBridgingRetain(generateHTMLData((__bridge NSURL*)url, bundle, thumb)); +} + +/*void renderThumbnail(CFURLRef url, CFBundleRef bundle, CGContextRef context, CGSize maxSize) { + NSData *data = generateHTMLData((__bridge NSURL*)url, bundle, true); + if (data) { + CGRect rect = CGRectMake(0, 0, 600, 800); + float scale = maxSize.height / rect.size.height; + + WebView *webView = [[WebView alloc] initWithFrame:rect]; + [webView.mainFrame.frameView scaleUnitSquareToSize:CGSizeMake(scale, scale)]; + [webView.mainFrame.frameView setAllowsScrolling:NO]; + [webView.mainFrame loadData:data MIMEType:@"text/html" textEncodingName:@"utf-8" baseURL:nil]; + + while ([webView isLoading]) + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); + [webView display]; + + NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)context + flipped:webView.isFlipped]; + [webView displayRectIgnoringOpacity:webView.bounds inContext:gc]; + } +}*/ diff --git a/QLOPML/style-thumb.css b/QLOPML/style-thumb.css new file mode 100644 index 0000000..3e1d73d --- /dev/null +++ b/QLOPML/style-thumb.css @@ -0,0 +1,4 @@ + +* { font-family: Courier; } +body { padding: 10px; margin-left: -1.5em; } +ul { padding: 0 0 1em 1.5em; list-style-type: none; }