├── .gitignore ├── LICENSE ├── QLOPML.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ └── QLOPML.xcscheme ├── QLOPML ├── GeneratePreviewForURL.c ├── GenerateThumbnailForURL.c ├── Info.plist ├── QLOPML.entitlements ├── main.c ├── opml-lib.h ├── opml-lib.m ├── style-thumb.css └── style.css ├── README.md ├── sample.opml └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/xcode,macos,objective-c 2 | 3 | ### macOS ### 4 | # General 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | ### Objective-C ### 32 | # Xcode 33 | # 34 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 35 | 36 | ## Build generated 37 | build/ 38 | DerivedData/ 39 | 40 | ## Various settings 41 | *.pbxuser 42 | !default.pbxuser 43 | *.mode1v3 44 | !default.mode1v3 45 | *.mode2v3 46 | !default.mode2v3 47 | *.perspectivev3 48 | !default.perspectivev3 49 | xcuserdata/ 50 | 51 | ## Other 52 | *.moved-aside 53 | *.xccheckout 54 | *.xcscmblueprint 55 | 56 | ## Obj-C/Swift specific 57 | *.hmap 58 | *.ipa 59 | *.dSYM.zip 60 | *.dSYM 61 | 62 | # CocoaPods 63 | # 64 | # We recommend against adding the Pods directory to your .gitignore. However 65 | # you should judge for yourself, the pros and cons are mentioned at: 66 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 67 | # 68 | # Pods/ 69 | # 70 | # Add this line if you want to avoid checking in source code from the Xcode workspace 71 | # *.xcworkspace 72 | 73 | # Carthage 74 | # 75 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 76 | Carthage/Checkouts 77 | 78 | Carthage/Build 79 | 80 | # fastlane 81 | # 82 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 83 | # screenshots whenever they are needed. 84 | # For more information about the recommended setup visit: 85 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 86 | 87 | fastlane/report.xml 88 | fastlane/Preview.html 89 | fastlane/screenshots/**/*.png 90 | fastlane/test_output 91 | 92 | # Code Injection 93 | # 94 | # After new code Injection tools there's a generated folder /iOSInjectionProject 95 | # https://github.com/johnno1962/injectionforxcode 96 | 97 | iOSInjectionProject/ 98 | 99 | ### Objective-C Patch ### 100 | 101 | ### Xcode ### 102 | # Xcode 103 | # 104 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 105 | 106 | ## User settings 107 | 108 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 109 | 110 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 111 | 112 | ### Xcode Patch ### 113 | *.xcodeproj/* 114 | !*.xcodeproj/project.pbxproj 115 | !*.xcodeproj/xcshareddata/ 116 | !*.xcworkspace/contents.xcworkspacedata 117 | /*.gcno 118 | 119 | 120 | # End of https://www.gitignore.io/api/xcode,macos,objective-c -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Oleg Geier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /QLOPML.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 540A649C22EE78B200470937 /* GenerateThumbnailForURL.c in Sources */ = {isa = PBXBuildFile; fileRef = 540A649B22EE78B200470937 /* GenerateThumbnailForURL.c */; }; 11 | 540A649E22EE78B200470937 /* GeneratePreviewForURL.c in Sources */ = {isa = PBXBuildFile; fileRef = 540A649D22EE78B200470937 /* GeneratePreviewForURL.c */; }; 12 | 540A64A022EE78B200470937 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 540A649F22EE78B200470937 /* main.c */; }; 13 | 541EF8B322EEFBEA00C415AA /* style.css in Resources */ = {isa = PBXBuildFile; fileRef = 541EF8B122EEFB2300C415AA /* style.css */; }; 14 | 54A75F6023D1269200754813 /* style-thumb.css in Resources */ = {isa = PBXBuildFile; fileRef = 54A75F5F23D1269100754813 /* style-thumb.css */; }; 15 | 54BFFC1123D09E7300012FBB /* opml-lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 54BFFC0F23D09E7300012FBB /* opml-lib.h */; }; 16 | 54BFFC1223D09E7300012FBB /* opml-lib.m in Sources */ = {isa = PBXBuildFile; fileRef = 54BFFC1023D09E7300012FBB /* opml-lib.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 540A649822EE78B200470937 /* QLOPML.qlgenerator */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QLOPML.qlgenerator; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 540A649B22EE78B200470937 /* GenerateThumbnailForURL.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = GenerateThumbnailForURL.c; sourceTree = ""; }; 22 | 540A649D22EE78B200470937 /* GeneratePreviewForURL.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = GeneratePreviewForURL.c; sourceTree = ""; }; 23 | 540A649F22EE78B200470937 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 24 | 540A64A122EE78B200470937 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 25 | 541EF8B122EEFB2300C415AA /* style.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = style.css; sourceTree = ""; }; 26 | 54A75F5F23D1269100754813 /* style-thumb.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = "style-thumb.css"; sourceTree = ""; }; 27 | 54BFFC0423D0988A00012FBB /* sample.opml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = sample.opml; sourceTree = SOURCE_ROOT; }; 28 | 54BFFC0F23D09E7300012FBB /* opml-lib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "opml-lib.h"; sourceTree = ""; }; 29 | 54BFFC1023D09E7300012FBB /* opml-lib.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "opml-lib.m"; sourceTree = ""; }; 30 | 54FB05D22305C8F400A088AD /* QLOPML.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QLOPML.entitlements; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 540A649522EE78B200470937 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 540A648E22EE78B200470937 = { 45 | isa = PBXGroup; 46 | children = ( 47 | 540A649A22EE78B200470937 /* QLOPML */, 48 | 540A649922EE78B200470937 /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 540A649922EE78B200470937 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 540A649822EE78B200470937 /* QLOPML.qlgenerator */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 540A649A22EE78B200470937 /* QLOPML */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 54FB05D22305C8F400A088AD /* QLOPML.entitlements */, 64 | 54BFFC0F23D09E7300012FBB /* opml-lib.h */, 65 | 54BFFC1023D09E7300012FBB /* opml-lib.m */, 66 | 540A649B22EE78B200470937 /* GenerateThumbnailForURL.c */, 67 | 540A649D22EE78B200470937 /* GeneratePreviewForURL.c */, 68 | 540A649F22EE78B200470937 /* main.c */, 69 | 540A64A122EE78B200470937 /* Info.plist */, 70 | 541EF8B122EEFB2300C415AA /* style.css */, 71 | 54A75F5F23D1269100754813 /* style-thumb.css */, 72 | 54BFFC0423D0988A00012FBB /* sample.opml */, 73 | ); 74 | path = QLOPML; 75 | sourceTree = ""; 76 | }; 77 | /* End PBXGroup section */ 78 | 79 | /* Begin PBXHeadersBuildPhase section */ 80 | 540A649322EE78B200470937 /* Headers */ = { 81 | isa = PBXHeadersBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | 54BFFC1123D09E7300012FBB /* opml-lib.h in Headers */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXHeadersBuildPhase section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | 540A649722EE78B200470937 /* QLOPML */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = 540A64A422EE78B200470937 /* Build configuration list for PBXNativeTarget "QLOPML" */; 94 | buildPhases = ( 95 | 540A649322EE78B200470937 /* Headers */, 96 | 540A649422EE78B200470937 /* Sources */, 97 | 540A649522EE78B200470937 /* Frameworks */, 98 | 540A649622EE78B200470937 /* Resources */, 99 | ); 100 | buildRules = ( 101 | ); 102 | dependencies = ( 103 | ); 104 | name = QLOPML; 105 | productName = QLOPML; 106 | productReference = 540A649822EE78B200470937 /* QLOPML.qlgenerator */; 107 | productType = "com.apple.product-type.bundle"; 108 | }; 109 | /* End PBXNativeTarget section */ 110 | 111 | /* Begin PBXProject section */ 112 | 540A648F22EE78B200470937 /* Project object */ = { 113 | isa = PBXProject; 114 | attributes = { 115 | LastUpgradeCheck = 1200; 116 | ORGANIZATIONNAME = relikd; 117 | TargetAttributes = { 118 | 540A649722EE78B200470937 = { 119 | CreatedOnToolsVersion = 10.0; 120 | SystemCapabilities = { 121 | com.apple.HardenedRuntime = { 122 | enabled = 1; 123 | }; 124 | com.apple.Sandbox = { 125 | enabled = 1; 126 | }; 127 | }; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = 540A649222EE78B200470937 /* Build configuration list for PBXProject "QLOPML" */; 132 | compatibilityVersion = "Xcode 9.3"; 133 | developmentRegion = en; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = 540A648E22EE78B200470937; 140 | productRefGroup = 540A649922EE78B200470937 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | 540A649722EE78B200470937 /* QLOPML */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | 540A649622EE78B200470937 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 54A75F6023D1269200754813 /* style-thumb.css in Resources */, 155 | 541EF8B322EEFBEA00C415AA /* style.css in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXSourcesBuildPhase section */ 162 | 540A649422EE78B200470937 /* Sources */ = { 163 | isa = PBXSourcesBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | 540A649C22EE78B200470937 /* GenerateThumbnailForURL.c in Sources */, 167 | 54BFFC1223D09E7300012FBB /* opml-lib.m in Sources */, 168 | 540A649E22EE78B200470937 /* GeneratePreviewForURL.c in Sources */, 169 | 540A64A022EE78B200470937 /* main.c in Sources */, 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | }; 173 | /* End PBXSourcesBuildPhase section */ 174 | 175 | /* Begin XCBuildConfiguration section */ 176 | 540A64A222EE78B200470937 /* Debug */ = { 177 | isa = XCBuildConfiguration; 178 | buildSettings = { 179 | ALWAYS_SEARCH_USER_PATHS = NO; 180 | CLANG_ANALYZER_NONNULL = YES; 181 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 182 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 183 | CLANG_CXX_LIBRARY = "libc++"; 184 | CLANG_ENABLE_MODULES = YES; 185 | CLANG_ENABLE_OBJC_ARC = YES; 186 | CLANG_ENABLE_OBJC_WEAK = YES; 187 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_COMMA = YES; 190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 191 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 193 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 194 | CLANG_WARN_EMPTY_BODY = YES; 195 | CLANG_WARN_ENUM_CONVERSION = YES; 196 | CLANG_WARN_INFINITE_RECURSION = YES; 197 | CLANG_WARN_INT_CONVERSION = YES; 198 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 199 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 200 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 201 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 202 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 203 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 204 | CLANG_WARN_STRICT_PROTOTYPES = YES; 205 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 206 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 207 | CLANG_WARN_UNREACHABLE_CODE = YES; 208 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 209 | CODE_SIGN_ENTITLEMENTS = QLOPML/QLOPML.entitlements; 210 | COPY_PHASE_STRIP = NO; 211 | DEBUG_INFORMATION_FORMAT = dwarf; 212 | ENABLE_HARDENED_RUNTIME = YES; 213 | ENABLE_STRICT_OBJC_MSGSEND = YES; 214 | ENABLE_TESTABILITY = YES; 215 | GCC_C_LANGUAGE_STANDARD = gnu11; 216 | GCC_DYNAMIC_NO_PIC = NO; 217 | GCC_NO_COMMON_BLOCKS = YES; 218 | GCC_OPTIMIZATION_LEVEL = 0; 219 | GCC_PREPROCESSOR_DEFINITIONS = ( 220 | "DEBUG=1", 221 | "$(inherited)", 222 | ); 223 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 224 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 225 | GCC_WARN_UNDECLARED_SELECTOR = YES; 226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 227 | GCC_WARN_UNUSED_FUNCTION = YES; 228 | GCC_WARN_UNUSED_VARIABLE = YES; 229 | MACOSX_DEPLOYMENT_TARGET = 10.9; 230 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 231 | MTL_FAST_MATH = YES; 232 | ONLY_ACTIVE_ARCH = YES; 233 | SDKROOT = macosx; 234 | }; 235 | name = Debug; 236 | }; 237 | 540A64A322EE78B200470937 /* Release */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ALWAYS_SEARCH_USER_PATHS = NO; 241 | CLANG_ANALYZER_NONNULL = YES; 242 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 243 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 244 | CLANG_CXX_LIBRARY = "libc++"; 245 | CLANG_ENABLE_MODULES = YES; 246 | CLANG_ENABLE_OBJC_ARC = YES; 247 | CLANG_ENABLE_OBJC_WEAK = YES; 248 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 249 | CLANG_WARN_BOOL_CONVERSION = YES; 250 | CLANG_WARN_COMMA = YES; 251 | CLANG_WARN_CONSTANT_CONVERSION = YES; 252 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 255 | CLANG_WARN_EMPTY_BODY = YES; 256 | CLANG_WARN_ENUM_CONVERSION = YES; 257 | CLANG_WARN_INFINITE_RECURSION = YES; 258 | CLANG_WARN_INT_CONVERSION = YES; 259 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 260 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 261 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 264 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 265 | CLANG_WARN_STRICT_PROTOTYPES = YES; 266 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 267 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | CODE_SIGN_ENTITLEMENTS = QLOPML/QLOPML.entitlements; 271 | COPY_PHASE_STRIP = NO; 272 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 273 | ENABLE_HARDENED_RUNTIME = YES; 274 | ENABLE_NS_ASSERTIONS = NO; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | GCC_C_LANGUAGE_STANDARD = gnu11; 277 | GCC_NO_COMMON_BLOCKS = YES; 278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 280 | GCC_WARN_UNDECLARED_SELECTOR = YES; 281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 282 | GCC_WARN_UNUSED_FUNCTION = YES; 283 | GCC_WARN_UNUSED_VARIABLE = YES; 284 | MACOSX_DEPLOYMENT_TARGET = 10.9; 285 | MTL_ENABLE_DEBUG_INFO = NO; 286 | MTL_FAST_MATH = YES; 287 | SDKROOT = macosx; 288 | }; 289 | name = Release; 290 | }; 291 | 540A64A522EE78B200470937 /* Debug */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | CODE_SIGN_IDENTITY = "Apple Development"; 295 | CODE_SIGN_STYLE = Automatic; 296 | DEVELOPMENT_TEAM = UY657LKNHJ; 297 | INFOPLIST_FILE = QLOPML/Info.plist; 298 | PRODUCT_BUNDLE_IDENTIFIER = de.relikd.QLOPML; 299 | PRODUCT_NAME = "$(TARGET_NAME)"; 300 | PROVISIONING_PROFILE_SPECIFIER = ""; 301 | WRAPPER_EXTENSION = qlgenerator; 302 | }; 303 | name = Debug; 304 | }; 305 | 540A64A622EE78B200470937 /* Release */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | CODE_SIGN_IDENTITY = "Apple Development"; 309 | CODE_SIGN_STYLE = Automatic; 310 | DEVELOPMENT_TEAM = UY657LKNHJ; 311 | INFOPLIST_FILE = QLOPML/Info.plist; 312 | PRODUCT_BUNDLE_IDENTIFIER = de.relikd.QLOPML; 313 | PRODUCT_NAME = "$(TARGET_NAME)"; 314 | PROVISIONING_PROFILE_SPECIFIER = ""; 315 | WRAPPER_EXTENSION = qlgenerator; 316 | }; 317 | name = Release; 318 | }; 319 | /* End XCBuildConfiguration section */ 320 | 321 | /* Begin XCConfigurationList section */ 322 | 540A649222EE78B200470937 /* Build configuration list for PBXProject "QLOPML" */ = { 323 | isa = XCConfigurationList; 324 | buildConfigurations = ( 325 | 540A64A222EE78B200470937 /* Debug */, 326 | 540A64A322EE78B200470937 /* Release */, 327 | ); 328 | defaultConfigurationIsVisible = 0; 329 | defaultConfigurationName = Release; 330 | }; 331 | 540A64A422EE78B200470937 /* Build configuration list for PBXNativeTarget "QLOPML" */ = { 332 | isa = XCConfigurationList; 333 | buildConfigurations = ( 334 | 540A64A522EE78B200470937 /* Debug */, 335 | 540A64A622EE78B200470937 /* Release */, 336 | ); 337 | defaultConfigurationIsVisible = 0; 338 | defaultConfigurationName = Release; 339 | }; 340 | /* End XCConfigurationList section */ 341 | }; 342 | rootObject = 540A648F22EE78B200470937 /* Project object */; 343 | } 344 | -------------------------------------------------------------------------------- /QLOPML.xcodeproj/xcshareddata/xcschemes/QLOPML.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 60 | 61 | 64 | 65 | 68 | 69 | 72 | 73 | 74 | 75 | 81 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /QLOPML/GeneratePreviewForURL.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "opml-lib.h" 5 | 6 | OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options); 7 | void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview); 8 | 9 | /* ----------------------------------------------------------------------------- 10 | Generate a preview for file 11 | 12 | This function's job is to create preview for designated file 13 | ----------------------------------------------------------------------------- */ 14 | 15 | OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) 16 | { 17 | CFBundleRef bundle = QLPreviewRequestGetGeneratorBundle(preview); 18 | CFDataRef data = generateHTML(url, bundle, false); 19 | if (data) { 20 | QLPreviewRequestSetDataRepresentation(preview, data, kUTTypeHTML, NULL); 21 | CFRelease(data); 22 | } 23 | return noErr; 24 | } 25 | 26 | void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview) 27 | { 28 | // Implement only if supported 29 | } 30 | -------------------------------------------------------------------------------- /QLOPML/GenerateThumbnailForURL.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "opml-lib.h" 5 | 6 | OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize); 7 | void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail); 8 | 9 | /* ----------------------------------------------------------------------------- 10 | Generate a thumbnail for file 11 | 12 | This function's job is to create thumbnail for designated file as fast as possible 13 | ----------------------------------------------------------------------------- */ 14 | 15 | OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize) 16 | { 17 | // test with: 18 | // 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 19 | CFBundleRef bundle = QLThumbnailRequestGetGeneratorBundle(thumbnail); 20 | CFDataRef data = generateHTML(url, bundle, true); 21 | if (data) { 22 | QLThumbnailRequestSetThumbnailWithDataRepresentation(thumbnail, data, kUTTypeHTML, NULL, NULL); 23 | CFRelease(data); 24 | } 25 | return noErr; 26 | 27 | // CGSize thumbSize = CGSizeMake(maxSize.width * (600/800.0), maxSize.height); 28 | // // Draw the webview in the correct context 29 | // CGContextRef context = QLThumbnailRequestCreateContext(thumbnail, thumbSize, false, NULL); 30 | // 31 | // if (context) { 32 | // CFBundleRef bundle = QLThumbnailRequestGetGeneratorBundle(thumbnail); 33 | // renderThumbnail(url, bundle, context, maxSize); 34 | // QLThumbnailRequestFlushContext(thumbnail, context); 35 | // CFRelease(context); 36 | // } 37 | // return noErr; 38 | } 39 | 40 | void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail) 41 | { 42 | // Implement only if supported 43 | } 44 | -------------------------------------------------------------------------------- /QLOPML/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeRole 11 | QLGenerator 12 | LSItemContentTypes 13 | 14 | org.opml.opml 15 | dyn.ah62d4rv4ge8086drru 16 | 17 | 18 | 19 | CFBundleExecutable 20 | $(EXECUTABLE_NAME) 21 | CFBundleIdentifier 22 | $(PRODUCT_BUNDLE_IDENTIFIER) 23 | CFBundleInfoDictionaryVersion 24 | 6.0 25 | CFBundleName 26 | $(PRODUCT_NAME) 27 | CFBundleShortVersionString 28 | 1.3 29 | CFBundleVersion 30 | 1 31 | CFPlugInDynamicRegisterFunction 32 | 33 | CFPlugInDynamicRegistration 34 | NO 35 | CFPlugInFactories 36 | 37 | FB471A28-C55C-4130-8499-31E890AF19C5 38 | QuickLookGeneratorPluginFactory 39 | 40 | CFPlugInTypes 41 | 42 | 5E2D9680-5022-40FA-B806-43349622E5B9 43 | 44 | FB471A28-C55C-4130-8499-31E890AF19C5 45 | 46 | 47 | CFPlugInUnloadFunction 48 | 49 | NSHumanReadableCopyright 50 | Copyright © 2019 relikd. Public Domain. 51 | QLNeedsToBeRunInMainThread 52 | 53 | QLPreviewHeight 54 | 600 55 | QLPreviewWidth 56 | 800 57 | QLSupportsConcurrentRequests 58 | 59 | QLThumbnailMinimumSize 60 | 17 61 | UTImportedTypeDeclarations 62 | 63 | 64 | UTTypeConformsTo 65 | 66 | public.xml 67 | 68 | UTTypeDescription 69 | OPML document 70 | UTTypeIdentifier 71 | org.opml.opml 72 | UTTypeReferenceURL 73 | http://dev.opml.org/spec2.html 74 | UTTypeTagSpecification 75 | 76 | public.filename-extension 77 | 78 | opml 79 | 80 | public.mime-type 81 | 82 | text/xml 83 | text/x-opml 84 | application/xml 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /QLOPML/QLOPML.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /QLOPML/main.c: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // 3 | // DO NO MODIFY THE CONTENT OF THIS FILE 4 | // 5 | // This file contains the generic CFPlug-in code necessary for your generator 6 | // To complete your generator implement the function in GenerateThumbnailForURL/GeneratePreviewForURL.c 7 | // 8 | //============================================================================== 9 | 10 | 11 | 12 | 13 | 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // ----------------------------------------------------------------------------- 21 | // constants 22 | // ----------------------------------------------------------------------------- 23 | 24 | // Don't modify this line 25 | #define PLUGIN_ID "6E0371E4-6852-4C6F-81DD-A7771F8159CC" 26 | 27 | // 28 | // Below is the generic glue code for all plug-ins. 29 | // 30 | // You should not have to modify this code aside from changing 31 | // names if you decide to change the names defined in the Info.plist 32 | // 33 | 34 | 35 | // ----------------------------------------------------------------------------- 36 | // typedefs 37 | // ----------------------------------------------------------------------------- 38 | 39 | // The thumbnail generation function to be implemented in GenerateThumbnailForURL.c 40 | OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize); 41 | void CancelThumbnailGeneration(void* thisInterface, QLThumbnailRequestRef thumbnail); 42 | 43 | // The preview generation function to be implemented in GeneratePreviewForURL.c 44 | OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options); 45 | void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview); 46 | 47 | // The layout for an instance of QuickLookGeneratorPlugIn 48 | typedef struct __QuickLookGeneratorPluginType 49 | { 50 | void *conduitInterface; 51 | CFUUIDRef factoryID; 52 | UInt32 refCount; 53 | } QuickLookGeneratorPluginType; 54 | 55 | // ----------------------------------------------------------------------------- 56 | // prototypes 57 | // ----------------------------------------------------------------------------- 58 | // Forward declaration for the IUnknown implementation. 59 | // 60 | 61 | QuickLookGeneratorPluginType *AllocQuickLookGeneratorPluginType(CFUUIDRef inFactoryID); 62 | void DeallocQuickLookGeneratorPluginType(QuickLookGeneratorPluginType *thisInstance); 63 | HRESULT QuickLookGeneratorQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv); 64 | void *QuickLookGeneratorPluginFactory(CFAllocatorRef allocator,CFUUIDRef typeID); 65 | ULONG QuickLookGeneratorPluginAddRef(void *thisInstance); 66 | ULONG QuickLookGeneratorPluginRelease(void *thisInstance); 67 | 68 | // ----------------------------------------------------------------------------- 69 | // myInterfaceFtbl definition 70 | // ----------------------------------------------------------------------------- 71 | // The QLGeneratorInterfaceStruct function table. 72 | // 73 | static QLGeneratorInterfaceStruct myInterfaceFtbl = { 74 | NULL, 75 | QuickLookGeneratorQueryInterface, 76 | QuickLookGeneratorPluginAddRef, 77 | QuickLookGeneratorPluginRelease, 78 | NULL, 79 | NULL, 80 | NULL, 81 | NULL 82 | }; 83 | 84 | 85 | // ----------------------------------------------------------------------------- 86 | // AllocQuickLookGeneratorPluginType 87 | // ----------------------------------------------------------------------------- 88 | // Utility function that allocates a new instance. 89 | // You can do some initial setup for the generator here if you wish 90 | // like allocating globals etc... 91 | // 92 | QuickLookGeneratorPluginType *AllocQuickLookGeneratorPluginType(CFUUIDRef inFactoryID) 93 | { 94 | QuickLookGeneratorPluginType *theNewInstance; 95 | 96 | theNewInstance = (QuickLookGeneratorPluginType *)malloc(sizeof(QuickLookGeneratorPluginType)); 97 | memset(theNewInstance,0,sizeof(QuickLookGeneratorPluginType)); 98 | 99 | /* Point to the function table Malloc enough to store the stuff and copy the filler from myInterfaceFtbl over */ 100 | theNewInstance->conduitInterface = malloc(sizeof(QLGeneratorInterfaceStruct)); 101 | memcpy(theNewInstance->conduitInterface,&myInterfaceFtbl,sizeof(QLGeneratorInterfaceStruct)); 102 | 103 | /* Retain and keep an open instance refcount for each factory. */ 104 | theNewInstance->factoryID = CFRetain(inFactoryID); 105 | CFPlugInAddInstanceForFactory(inFactoryID); 106 | 107 | /* This function returns the IUnknown interface so set the refCount to one. */ 108 | theNewInstance->refCount = 1; 109 | return theNewInstance; 110 | } 111 | 112 | // ----------------------------------------------------------------------------- 113 | // DeallocQuickLookGeneratorPluginType 114 | // ----------------------------------------------------------------------------- 115 | // Utility function that deallocates the instance when 116 | // the refCount goes to zero. 117 | // In the current implementation generator interfaces are never deallocated 118 | // but implement this as this might change in the future 119 | // 120 | void DeallocQuickLookGeneratorPluginType(QuickLookGeneratorPluginType *thisInstance) 121 | { 122 | CFUUIDRef theFactoryID; 123 | 124 | theFactoryID = thisInstance->factoryID; 125 | /* Free the conduitInterface table up */ 126 | free(thisInstance->conduitInterface); 127 | 128 | /* Free the instance structure */ 129 | free(thisInstance); 130 | if (theFactoryID){ 131 | CFPlugInRemoveInstanceForFactory(theFactoryID); 132 | CFRelease(theFactoryID); 133 | } 134 | } 135 | 136 | // ----------------------------------------------------------------------------- 137 | // QuickLookGeneratorQueryInterface 138 | // ----------------------------------------------------------------------------- 139 | // Implementation of the IUnknown QueryInterface function. 140 | // 141 | HRESULT QuickLookGeneratorQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv) 142 | { 143 | CFUUIDRef interfaceID; 144 | 145 | interfaceID = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault,iid); 146 | 147 | if (CFEqual(interfaceID,kQLGeneratorCallbacksInterfaceID)){ 148 | /* If the Right interface was requested, bump the ref count, 149 | * set the ppv parameter equal to the instance, and 150 | * return good status. 151 | */ 152 | ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->GenerateThumbnailForURL = GenerateThumbnailForURL; 153 | ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->CancelThumbnailGeneration = CancelThumbnailGeneration; 154 | ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->GeneratePreviewForURL = GeneratePreviewForURL; 155 | ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->CancelPreviewGeneration = CancelPreviewGeneration; 156 | ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType*)thisInstance)->conduitInterface)->AddRef(thisInstance); 157 | *ppv = thisInstance; 158 | CFRelease(interfaceID); 159 | return S_OK; 160 | }else{ 161 | /* Requested interface unknown, bail with error. */ 162 | *ppv = NULL; 163 | CFRelease(interfaceID); 164 | return E_NOINTERFACE; 165 | } 166 | } 167 | 168 | // ----------------------------------------------------------------------------- 169 | // QuickLookGeneratorPluginAddRef 170 | // ----------------------------------------------------------------------------- 171 | // Implementation of reference counting for this type. Whenever an interface 172 | // is requested, bump the refCount for the instance. NOTE: returning the 173 | // refcount is a convention but is not required so don't rely on it. 174 | // 175 | ULONG QuickLookGeneratorPluginAddRef(void *thisInstance) 176 | { 177 | ((QuickLookGeneratorPluginType *)thisInstance )->refCount += 1; 178 | return ((QuickLookGeneratorPluginType*) thisInstance)->refCount; 179 | } 180 | 181 | // ----------------------------------------------------------------------------- 182 | // QuickLookGeneratorPluginRelease 183 | // ----------------------------------------------------------------------------- 184 | // When an interface is released, decrement the refCount. 185 | // If the refCount goes to zero, deallocate the instance. 186 | // 187 | ULONG QuickLookGeneratorPluginRelease(void *thisInstance) 188 | { 189 | ((QuickLookGeneratorPluginType*)thisInstance)->refCount -= 1; 190 | if (((QuickLookGeneratorPluginType*)thisInstance)->refCount == 0){ 191 | DeallocQuickLookGeneratorPluginType((QuickLookGeneratorPluginType*)thisInstance ); 192 | return 0; 193 | }else{ 194 | return ((QuickLookGeneratorPluginType*) thisInstance )->refCount; 195 | } 196 | } 197 | 198 | // ----------------------------------------------------------------------------- 199 | // QuickLookGeneratorPluginFactory 200 | // ----------------------------------------------------------------------------- 201 | void *QuickLookGeneratorPluginFactory(CFAllocatorRef allocator,CFUUIDRef typeID) 202 | { 203 | QuickLookGeneratorPluginType *result; 204 | CFUUIDRef uuid; 205 | 206 | /* If correct type is being requested, allocate an 207 | * instance of kQLGeneratorTypeID and return the IUnknown interface. 208 | */ 209 | if (CFEqual(typeID,kQLGeneratorTypeID)){ 210 | uuid = CFUUIDCreateFromString(kCFAllocatorDefault,CFSTR(PLUGIN_ID)); 211 | result = AllocQuickLookGeneratorPluginType(uuid); 212 | CFRelease(uuid); 213 | return result; 214 | } 215 | /* If the requested type is incorrect, return NULL. */ 216 | return NULL; 217 | } 218 | 219 | -------------------------------------------------------------------------------- /QLOPML/opml-lib.h: -------------------------------------------------------------------------------- 1 | #ifndef opml_lib_h 2 | #define opml_lib_h 3 | 4 | CFDataRef generateHTML(CFURLRef url, CFBundleRef bundle, Boolean thumb); 5 | //void renderThumbnail(CFURLRef url, CFBundleRef bundle, CGContextRef context, CGSize maxSize); 6 | 7 | #endif /* opml_lib_h */ 8 | -------------------------------------------------------------------------------- /QLOPML/opml-lib.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | //#import 4 | 5 | // --------------------------------------------------------------- 6 | // | 7 | // | OPML renderer 8 | // | 9 | // --------------------------------------------------------------- 10 | 11 | NSXMLElement* make(NSString *tag, NSString *text, NSXMLElement *parent) { 12 | NSXMLElement *div = [NSXMLElement elementWithName:tag]; 13 | if (text) div.stringValue = text; 14 | [parent addChild:div]; 15 | return div; 16 | } 17 | 18 | void attribute(NSXMLElement *parent, NSString *key, NSString *value) { 19 | [parent addAttribute:[NSXMLElement attributeWithName:key stringValue:value]]; 20 | } 21 | 22 | NSXMLElement* section(NSString *title, NSString *container, NSXMLElement *parent) { 23 | make(@"h3", title, parent); 24 | NSXMLElement *div = make(container, nil, parent); 25 | attribute(div, @"class", @"section"); 26 | return div; 27 | } 28 | 29 | void appendNode(NSXMLElement *child, NSXMLElement *parent, Boolean thumb) { 30 | 31 | if ([child.name isEqualToString:@"head"]) { 32 | if (thumb) 33 | return; 34 | NSXMLElement *dl = section(@"Metadata:", @"dl", parent); 35 | for (NSXMLElement *head in child.children) { 36 | make(@"dt", head.name, dl); 37 | make(@"dd", head.stringValue, dl); 38 | } 39 | return; 40 | } 41 | 42 | if ([child.name isEqualToString:@"body"]) { 43 | parent = thumb ? make(@"ul", nil, parent) : section(@"Content:", @"ul", parent); 44 | 45 | } else if ([child.name isEqualToString:@"outline"]) { 46 | if ([child attributeForName:@"separator"].stringValue) { 47 | make(@"hr", nil, parent); 48 | } else { 49 | NSString *desc = [child attributeForName:@"title"].stringValue; 50 | if (!desc || desc.length == 0) 51 | desc = [child attributeForName:@"text"].stringValue; 52 | // refreshInterval 53 | NSXMLElement *li = make(@"li", desc, parent); 54 | if (!thumb) { 55 | NSString *xmlUrl = [child attributeForName:@"xmlUrl"].stringValue; 56 | if (xmlUrl && xmlUrl.length > 0) { 57 | [li addChild:[NSXMLNode textWithStringValue:@" — "]]; 58 | attribute(make(@"a", xmlUrl, li), @"href", xmlUrl); 59 | } 60 | } 61 | } 62 | if (child.childCount > 0) { 63 | parent = make(@"ul", nil, parent); 64 | } 65 | } 66 | for (NSXMLElement *c in child.children) { 67 | appendNode(c, parent, thumb); 68 | } 69 | } 70 | 71 | NSData* generateHTMLData(NSURL *url, CFBundleRef bundle, Boolean thumb) { 72 | NSError *err; 73 | NSXMLDocument *doc = [[NSXMLDocument alloc] initWithContentsOfURL:url options:0 error:&err]; 74 | if (err || !doc) { 75 | printf("ERROR: %s\n", err.description.UTF8String); 76 | return nil; 77 | } 78 | 79 | NSXMLElement *html = [NSXMLElement elementWithName:@"html"]; 80 | NSXMLElement *head = make(@"head", nil, html); 81 | make(@"title", @"OPML file", head); 82 | 83 | CFURLRef path = CFBundleCopyResourceURL(bundle, thumb ? CFSTR("style-thumb") : CFSTR("style"), CFSTR("css"), NULL); 84 | NSString *data = [NSString stringWithContentsOfFile:CFBridgingRelease(path) encoding:NSUTF8StringEncoding error:nil]; 85 | make(@"style", data, head); 86 | 87 | NSXMLElement *body = make(@"body", nil, html); 88 | 89 | for (NSXMLElement *child in doc.children) { 90 | appendNode(child, body, thumb); 91 | } 92 | NSXMLDocument *xml = [NSXMLDocument documentWithRootElement:html]; 93 | return [xml XMLDataWithOptions:NSXMLNodePrettyPrint | NSXMLNodeCompactEmptyElement]; 94 | } 95 | 96 | CFDataRef generateHTML(CFURLRef url, CFBundleRef bundle, Boolean thumb) { 97 | return CFBridgingRetain(generateHTMLData((__bridge NSURL*)url, bundle, thumb)); 98 | } 99 | 100 | /*void renderThumbnail(CFURLRef url, CFBundleRef bundle, CGContextRef context, CGSize maxSize) { 101 | NSData *data = generateHTMLData((__bridge NSURL*)url, bundle, true); 102 | if (data) { 103 | CGRect rect = CGRectMake(0, 0, 600, 800); 104 | float scale = maxSize.height / rect.size.height; 105 | 106 | WebView *webView = [[WebView alloc] initWithFrame:rect]; 107 | [webView.mainFrame.frameView scaleUnitSquareToSize:CGSizeMake(scale, scale)]; 108 | [webView.mainFrame.frameView setAllowsScrolling:NO]; 109 | [webView.mainFrame loadData:data MIMEType:@"text/html" textEncodingName:@"utf-8" baseURL:nil]; 110 | 111 | while ([webView isLoading]) 112 | CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); 113 | [webView display]; 114 | 115 | NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)context 116 | flipped:webView.isFlipped]; 117 | [webView displayRectIgnoringOpacity:webView.bounds inContext:gc]; 118 | } 119 | }*/ 120 | -------------------------------------------------------------------------------- /QLOPML/style-thumb.css: -------------------------------------------------------------------------------- 1 | 2 | * { font-family: Courier; } 3 | body { padding: 10px; margin-left: -1.5em; } 4 | ul { padding: 0 0 1em 1.5em; list-style-type: none; } 5 | -------------------------------------------------------------------------------- /QLOPML/style.css: -------------------------------------------------------------------------------- 1 | 2 | * { font-family: Courier; } 3 | body { padding: 30px; background-color: #AAA; color: black; } 4 | dd, li, hr { font-weight: bold; line-height: 1.5em; } 5 | ul { list-style-type: none; padding-bottom: 1em; } 6 | a { font-size: 0.75em; color: #FBA43A; } 7 | .section { padding: 1em 1.5em; border-radius: 7px; background-color: #EEE; } 8 | 9 | @media (prefers-color-scheme: dark) { 10 | body { background-color: #555; color: white; } 11 | .section { background-color: #222; } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QLOPML 2 | 3 | Quick Look generator for `.opml` files. 4 | Optimized for RSS reader outline files. E.g., see my other project [baRSS](https://github.com/relikd/baRSS). 5 | 6 | 7 | ![screenshot](screenshot.png) 8 | 9 | ## Install 10 | 11 | 1) Build project or download [lastest release](https://github.com/relikd/QLOPML/releases/latest) 12 | 2) Copy `QLOPML.qlgenerator` to either `/Library/QuickLook` (system wide) or `~/Library/QuickLook` (user) 13 | 3) Run `qlmanage -r` to update Quick Look cache. 14 | 15 | ## License 16 | 17 | [MIT License](https://opensource.org/licenses/MIT) 18 | -------------------------------------------------------------------------------- /sample.opml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample OPML file for RSSReader 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relikd/QLOPML/6e4cc5c7b08b2630c998fd6ab932b7ed2b1a4f60/screenshot.png --------------------------------------------------------------------------------