├── .gitignore ├── License.txt ├── Metadata.txt ├── Metadata ├── Screenshot-12.9 │ ├── 1.png │ ├── 2.png │ └── 3.png ├── Screenshot-5.5 │ ├── 1.png │ ├── 2.png │ └── 3.png └── Screenshot-6.5 │ ├── 1.png │ ├── 2.png │ └── 3.png ├── README.md ├── xFonts.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── manolo.xcuserdatad │ └── xcschemes │ ├── xFonts.xcscheme │ └── xcschememanagement.plist └── xFonts ├── AboutViewController.h ├── AboutViewController.m ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ ├── AppStore-1024.png │ ├── Contents.json │ ├── iPad-76.png │ ├── iPad-76@2x.png │ ├── iPadPro-83.5@2x.png │ ├── iPadSettings-29.png │ ├── iPadSettings-29@2x.png │ ├── iPadSpotlight-40.png │ ├── iPadSpotlight-40@2x.png │ ├── iPhone-60@2x.png │ ├── iPhone-60@3x.png │ ├── iPhoneSettings-29@2x.png │ ├── iPhoneSettings-29@3x.png │ ├── iPhoneSpotlight-40@2x.png │ └── iPhoneSpotlight-40@3x.png ├── Colors │ ├── Contents.json │ ├── appHeaderBackground.colorset │ │ └── Contents.json │ ├── appHeaderText.colorset │ │ └── Contents.json │ ├── appHeaderTint.colorset │ │ └── Contents.json │ ├── appTint.colorset │ │ └── Contents.json │ ├── infoBackground.colorset │ │ └── Contents.json │ ├── infoHighightSecondary.colorset │ │ └── Contents.json │ ├── infoHighlight.colorset │ │ └── Contents.json │ └── overlayBackground.colorset │ │ └── Contents.json ├── Contents.json └── Graphics │ ├── Contents.json │ ├── arrowhead.imageset │ ├── Contents.json │ ├── arrowhead@1x.png │ ├── arrowhead@2x.png │ └── arrowhead@3x.png │ ├── iconfactory_logo.imageset │ ├── Contents.json │ ├── if_logo.png │ ├── if_logo@2x.png │ └── if_logo@3x.png │ └── product_logo.imageset │ ├── Contents.json │ └── Fontcase-logo.png ├── Base.lproj └── Main.storyboard ├── DebugLog.h ├── DetailViewController.h ├── DetailViewController.m ├── FontInfo.h ├── FontInfo.m ├── HeaderView.h ├── HeaderView.m ├── Help.md ├── HelpViewController.h ├── HelpViewController.m ├── Info.plist ├── MarkdownAttributedString ├── NSAttributedString+Markdown.h ├── NSAttributedString+Markdown.m └── README.md ├── NavigationController.h ├── NavigationController.m ├── RoutingHTTPServer ├── CocoaHTTPServer │ ├── Core │ │ ├── Categories │ │ │ ├── DDData.h │ │ │ ├── DDData.m │ │ │ ├── DDNumber.h │ │ │ ├── DDNumber.m │ │ │ ├── DDRange.h │ │ │ └── DDRange.m │ │ ├── HTTPAuthenticationRequest.h │ │ ├── HTTPAuthenticationRequest.m │ │ ├── HTTPConnection.h │ │ ├── HTTPConnection.m │ │ ├── HTTPLogging.h │ │ ├── HTTPMessage.h │ │ ├── HTTPMessage.m │ │ ├── HTTPResponse.h │ │ ├── HTTPServer.h │ │ ├── HTTPServer.m │ │ ├── Mime │ │ │ ├── MultipartFormDataParser.h │ │ │ ├── MultipartFormDataParser.m │ │ │ ├── MultipartMessageHeader.h │ │ │ ├── MultipartMessageHeader.m │ │ │ ├── MultipartMessageHeaderField.h │ │ │ └── MultipartMessageHeaderField.m │ │ ├── Responses │ │ │ ├── HTTPAsyncFileResponse.h │ │ │ ├── HTTPAsyncFileResponse.m │ │ │ ├── HTTPDataResponse.h │ │ │ ├── HTTPDataResponse.m │ │ │ ├── HTTPDynamicFileResponse.h │ │ │ ├── HTTPDynamicFileResponse.m │ │ │ ├── HTTPErrorResponse.h │ │ │ ├── HTTPErrorResponse.m │ │ │ ├── HTTPFileResponse.h │ │ │ ├── HTTPFileResponse.m │ │ │ ├── HTTPRedirectResponse.h │ │ │ └── HTTPRedirectResponse.m │ │ ├── WebSocket.h │ │ └── WebSocket.m │ ├── LICENSE.txt │ ├── README.markdown │ └── Vendor │ │ ├── CocoaAsyncSocket │ │ ├── About.txt │ │ ├── GCDAsyncSocket.h │ │ └── GCDAsyncSocket.m │ │ └── CocoaLumberjack │ │ ├── About.txt │ │ ├── DDASLLogger.h │ │ ├── DDASLLogger.m │ │ ├── DDAbstractDatabaseLogger.h │ │ ├── DDAbstractDatabaseLogger.m │ │ ├── DDFileLogger.h │ │ ├── DDFileLogger.m │ │ ├── DDLog.h │ │ ├── DDLog.m │ │ ├── DDTTYLogger.h │ │ ├── DDTTYLogger.m │ │ └── Extensions │ │ ├── ContextFilterLogFormatter.h │ │ ├── ContextFilterLogFormatter.m │ │ ├── DispatchQueueLogFormatter.h │ │ ├── DispatchQueueLogFormatter.m │ │ └── README.txt ├── HTTPResponseProxy.h ├── HTTPResponseProxy.m ├── LICENSE.txt ├── README.md ├── Route.h ├── Route.m ├── RouteRequest.h ├── RouteRequest.m ├── RouteResponse.h ├── RouteResponse.m ├── RoutingConnection.h ├── RoutingConnection.m ├── RoutingHTTPServer.h └── RoutingHTTPServer.m ├── TabBarController.h ├── TabBarController.m ├── ViewController.h ├── ViewController.m ├── iTunes Screen Shot.psd ├── index.html ├── main.m ├── xFonts Icon.sketch └── xFonts └── Base.lproj └── Launch Screen.storyboard /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | IDEWorkspaceChecks.plist 20 | 21 | # AppCode 22 | code/.idea 23 | 24 | .DS_Store 25 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 manolo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Metadata.txt: -------------------------------------------------------------------------------- 1 | App Name: Fontcase - Manage Your Type 2 | 3 | Privacy Policy URL: https://iconfactory.com/privacy 4 | 5 | Keywords: font,fonts,install,custom,type,xfonts,xfont,icloud,dropbox,personalize 6 | 7 | Support URL: https://support.iconfactory.com 8 | 9 | Marketing URL: https://iconfactoryapps.com 10 | 11 | Copyright: © 2009-2020 Manolo Sañudo 12 | 13 | Description: 14 | 15 | Fontcase is an app that lets you install custom fonts on iOS and iPadOS. 16 | 17 | 1) Import fonts from your iCloud Drive or Dropbox and add them to your collection of favorites. 18 | 19 | 2) Tap on any listed font and you'll get a preview of the characters along with other metadata. 20 | 21 | 3) When you're done collecting, install your fonts in Settings and they'll be available in other apps, including iWork, to make your documents look better than ever. 22 | 23 | Fontcase is completely open source, so your privacy is ensured. The app only adds fonts to your settings and does not modify them at all in the process. For more information on why this is important, see the app's Help tab. 24 | 25 | Enjoy! 26 | 27 | What's New: 28 | 29 | • xFonts is now Fontcase - new branding to avoid confusion with another font app 30 | • Now supports installing fonts from iCloud Drive and other Files storage locations like Dropbox 31 | • An imported font can be previewed with its metadata 32 | • Extensive help explaining the font installation process 33 | • Support for Light and Dark Mode in iOS 34 | • Adaptive layout that works well on all devices including iPad Pros to iPhone SE 35 | 36 | Screenshots: 37 | 38 | 1242 x 2688 in Screenshot-6.5 39 | 40 | 1242 x 2208 in Screenshot-5.5 41 | 42 | 2048 x 2732 in Screenshot-12.9 43 | 44 | -------------------------------------------------------------------------------- /Metadata/Screenshot-12.9/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-12.9/1.png -------------------------------------------------------------------------------- /Metadata/Screenshot-12.9/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-12.9/2.png -------------------------------------------------------------------------------- /Metadata/Screenshot-12.9/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-12.9/3.png -------------------------------------------------------------------------------- /Metadata/Screenshot-5.5/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-5.5/1.png -------------------------------------------------------------------------------- /Metadata/Screenshot-5.5/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-5.5/2.png -------------------------------------------------------------------------------- /Metadata/Screenshot-5.5/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-5.5/3.png -------------------------------------------------------------------------------- /Metadata/Screenshot-6.5/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-6.5/1.png -------------------------------------------------------------------------------- /Metadata/Screenshot-6.5/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-6.5/2.png -------------------------------------------------------------------------------- /Metadata/Screenshot-6.5/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/Metadata/Screenshot-6.5/3.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xFonts 2 | 3 | xFonts is an app that lets you install custom fonts on iOS and iPadOS. 4 | 5 | 1. Grab fonts from your iCloud Drive or Dropbox and add them to your collection of favorites. 6 | 2. Tap on any font and you'll get a preview of the characters along with other metadata. 7 | 3. When you're done collecting, install your fonts in Settings and they'll be available in other apps, including iWork, to make your documents look better than ever. 8 | 9 | xFonts is completely open source, so your privacy is insured. The app only adds fonts to your settings and does not modify them at all in the process. For more information on why this is important, see the app's Help screen. 10 | 11 | If you don't want to build this app yourself, it's available as [Fontcase on the App Store](https://apps.apple.com/us/app/id1205074470?ls=1&mt=12&uo=4&pt=8934&at=10l4G7&ct=GITHUB_SITE). 12 | 13 | Enjoy! 14 | -------------------------------------------------------------------------------- /xFonts.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /xFonts.xcodeproj/xcuserdata/manolo.xcuserdatad/xcschemes/xFonts.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /xFonts.xcodeproj/xcuserdata/manolo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | xFonts.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 93AE4C671E42C44B00D19E41 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /xFonts/AboutViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AboutViewController.h 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/19/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface AboutViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /xFonts/AboutViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AboutViewController.m 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/19/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import "AboutViewController.h" 10 | 11 | @interface AboutViewController () 12 | 13 | @property (nonatomic, weak) IBOutlet UILabel *versionLabel; 14 | 15 | @end 16 | 17 | @implementation AboutViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | 23 | NSString *productVersion = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; 24 | NSString *productBuild = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; 25 | self.versionLabel.text = [NSString stringWithFormat:@"VERSION %@ (%@)", productVersion, productBuild]; 26 | } 27 | 28 | - (IBAction)openIconfactory:(id)sender 29 | { 30 | [UIApplication.sharedApplication openURL:[NSURL URLWithString:@"https://iconfactory.com"] options:@{} completionHandler:nil]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /xFonts/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // xFonts 4 | // 5 | // Created by manolo on 2/1/17. 6 | // Copyright © 2017 manolo. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /xFonts/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // xFonts 4 | // 5 | // Created by manolo on 2/1/17. 6 | // Copyright © 2017 manolo. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | return YES; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/AppStore-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/AppStore-1024.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "iPhoneSettings-29@2x.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "iPhoneSettings-29@3x.png", 23 | "scale" : "3x" 24 | }, 25 | { 26 | "size" : "40x40", 27 | "idiom" : "iphone", 28 | "filename" : "iPhoneSpotlight-40@2x.png", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "iPhoneSpotlight-40@3x.png", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "size" : "60x60", 39 | "idiom" : "iphone", 40 | "filename" : "iPhone-60@2x.png", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "iPhone-60@3x.png", 47 | "scale" : "3x" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "size" : "20x20", 52 | "scale" : "1x" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "size" : "20x20", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "size" : "29x29", 61 | "idiom" : "ipad", 62 | "filename" : "iPadSettings-29.png", 63 | "scale" : "1x" 64 | }, 65 | { 66 | "size" : "29x29", 67 | "idiom" : "ipad", 68 | "filename" : "iPadSettings-29@2x.png", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "size" : "40x40", 73 | "idiom" : "ipad", 74 | "filename" : "iPadSpotlight-40.png", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "size" : "40x40", 79 | "idiom" : "ipad", 80 | "filename" : "iPadSpotlight-40@2x.png", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "size" : "76x76", 85 | "idiom" : "ipad", 86 | "filename" : "iPad-76.png", 87 | "scale" : "1x" 88 | }, 89 | { 90 | "size" : "76x76", 91 | "idiom" : "ipad", 92 | "filename" : "iPad-76@2x.png", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "size" : "83.5x83.5", 97 | "idiom" : "ipad", 98 | "filename" : "iPadPro-83.5@2x.png", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "size" : "1024x1024", 103 | "idiom" : "ios-marketing", 104 | "filename" : "AppStore-1024.png", 105 | "scale" : "1x" 106 | } 107 | ], 108 | "info" : { 109 | "version" : 1, 110 | "author" : "xcode" 111 | } 112 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPad-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPad-76.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPad-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPad-76@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPadPro-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPadPro-83.5@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPadSettings-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPadSettings-29.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPadSettings-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPadSettings-29@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPadSpotlight-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPadSpotlight-40.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPadSpotlight-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPadSpotlight-40@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPhone-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPhone-60@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPhone-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPhone-60@3x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSettings-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSettings-29@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSettings-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSettings-29@3x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight-40@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/AppIcon.appiconset/iPhoneSpotlight-40@3x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/appHeaderBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xA0", 13 | "alpha" : "1.000", 14 | "blue" : "0xDD", 15 | "green" : "0x86" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "dark" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0xA0", 31 | "alpha" : "1.000", 32 | "blue" : "0xDD", 33 | "green" : "0x86" 34 | } 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/appHeaderText.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xEC", 10 | "red" : "0xF1" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xFF", 27 | "green" : "0xEC", 28 | "red" : "0xF1" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/appHeaderTint.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xFF", 27 | "green" : "0xFF", 28 | "red" : "0xFF" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/appTint.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0x77", 13 | "alpha" : "1.000", 14 | "blue" : "0xBA", 15 | "green" : "0x5B" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "dark" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0xA0", 31 | "alpha" : "1.000", 32 | "blue" : "0xDD", 33 | "green" : "0x86" 34 | } 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/infoBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0x77", 13 | "alpha" : "1.000", 14 | "blue" : "0xBA", 15 | "green" : "0x5B" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "dark" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0x77", 31 | "alpha" : "1.000", 32 | "blue" : "0xBA", 33 | "green" : "0x5B" 34 | } 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/infoHighightSecondary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xFF", 13 | "alpha" : "0.750", 14 | "blue" : "0xFF", 15 | "green" : "0xFF" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/infoHighlight.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xFF", 13 | "alpha" : "1.000", 14 | "blue" : "0xFF", 15 | "green" : "0xFF" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Colors/overlayBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0x00", 13 | "alpha" : "0.600", 14 | "blue" : "0x00", 15 | "green" : "0x00" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "dark" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0x00", 31 | "alpha" : "0.500", 32 | "blue" : "0x00", 33 | "green" : "0x00" 34 | } 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/arrowhead.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "arrowhead@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "arrowhead@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "arrowhead@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/arrowhead.imageset/arrowhead@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/Graphics/arrowhead.imageset/arrowhead@1x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/arrowhead.imageset/arrowhead@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/Graphics/arrowhead.imageset/arrowhead@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/arrowhead.imageset/arrowhead@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/Graphics/arrowhead.imageset/arrowhead@3x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/iconfactory_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "if_logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "if_logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "if_logo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/iconfactory_logo.imageset/if_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/Graphics/iconfactory_logo.imageset/if_logo.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/iconfactory_logo.imageset/if_logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/Graphics/iconfactory_logo.imageset/if_logo@2x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/iconfactory_logo.imageset/if_logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/Graphics/iconfactory_logo.imageset/if_logo@3x.png -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/product_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Fontcase-logo.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /xFonts/Assets.xcassets/Graphics/product_logo.imageset/Fontcase-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/Assets.xcassets/Graphics/product_logo.imageset/Fontcase-logo.png -------------------------------------------------------------------------------- /xFonts/DebugLog.h: -------------------------------------------------------------------------------- 1 | // Created by Sean Heber on 6/24/10. 2 | 3 | #ifdef DEBUG 4 | #define DebugLog(...) NSLog(__VA_ARGS__) 5 | #else 6 | #define DebugLog(...) do {} while (0) 7 | #endif 8 | 9 | #define ReleaseLog(...) NSLog(__VA_ARGS__) 10 | 11 | #define DEBUG_AUTO_LAYOUT 0 12 | #if DEBUG_AUTO_LAYOUT 13 | #define AutoLayoutLog(...) NSLog(__VA_ARGS__) 14 | #else 15 | #define AutoLayoutLog(...) do {} while (0) 16 | #endif 17 | -------------------------------------------------------------------------------- /xFonts/DetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.h 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/18/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "FontInfo.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface DetailViewController : UIViewController 16 | 17 | @property (nonatomic, strong) FontInfo *fontInfo; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /xFonts/DetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.m 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/18/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import "DetailViewController.h" 10 | 11 | @interface DetailViewController () 12 | 13 | @property (nonatomic, weak) IBOutlet UILabel *fileNameLabel; 14 | @property (nonatomic, weak) IBOutlet UILabel *postScriptNameLabel; 15 | @property (nonatomic, weak) IBOutlet UILabel *displayNameLabel; 16 | @property (nonatomic, weak) IBOutlet UILabel *familyNameLabel; 17 | @property (nonatomic, weak) IBOutlet UILabel *styleNameLabel; 18 | @property (nonatomic, weak) IBOutlet UILabel *monospacedLabel; 19 | @property (nonatomic, weak) IBOutlet UILabel *versionNameLabel; 20 | @property (nonatomic, weak) IBOutlet UILabel *glyphCountLabel; 21 | @property (nonatomic, weak) IBOutlet UILabel *copyrightLabel; 22 | @property (nonatomic, weak) IBOutlet UILabel *descriptionLabel; 23 | 24 | 25 | @property (nonatomic, weak) IBOutlet UIView *versionView; 26 | @property (nonatomic, weak) IBOutlet UIView *copyrightView; 27 | @property (nonatomic, weak) IBOutlet UIView *descriptionView; 28 | 29 | @property (nonatomic, weak) IBOutlet UIView *installWarningView; 30 | @property (nonatomic, weak) IBOutlet UIView *sampleBackgroundView; 31 | @property (nonatomic, weak) IBOutlet UITextView *sampleTextView; 32 | 33 | @end 34 | 35 | @implementation DetailViewController 36 | 37 | - (void)viewDidLoad 38 | { 39 | [super viewDidLoad]; 40 | // Do any additional setup after loading the view. 41 | 42 | [self updateView]; 43 | } 44 | 45 | /* 46 | #pragma mark - Navigation 47 | 48 | // In a storyboard-based application, you will often want to do a little preparation before navigation 49 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 50 | // Get the new view controller using [segue destinationViewController]. 51 | // Pass the selected object to the new view controller. 52 | } 53 | */ 54 | 55 | #pragma mark - Utility 56 | 57 | - (void)updateView 58 | { 59 | self.fileNameLabel.text = self.fontInfo.fileName; 60 | self.postScriptNameLabel.text = self.fontInfo.postScriptName; 61 | self.displayNameLabel.text = self.fontInfo.displayName; 62 | self.familyNameLabel.text = self.fontInfo.familyName; 63 | self.styleNameLabel.text = self.fontInfo.styleName; 64 | 65 | self.monospacedLabel.text = (self.fontInfo.isMonospaced ? @"Yes" : @"No"); 66 | 67 | self.glyphCountLabel.text = [NSNumberFormatter localizedStringFromNumber:@(self.fontInfo.numberOfGlyphs) numberStyle:NSNumberFormatterDecimalStyle]; 68 | if (self.fontInfo.versionName) { 69 | self.versionNameLabel.text = self.fontInfo.versionName; 70 | } 71 | else { 72 | self.versionView.hidden = YES; 73 | } 74 | if (self.fontInfo.copyrightName) { 75 | self.copyrightLabel.text = self.fontInfo.copyrightName; 76 | } 77 | else { 78 | self.copyrightView.hidden = YES; 79 | } 80 | if (self.fontInfo.descriptionName) { 81 | self.descriptionLabel.text = self.fontInfo.descriptionName; 82 | } 83 | else { 84 | self.descriptionView.hidden = YES; 85 | } 86 | 87 | UIFont *font = [UIFont fontWithName:self.fontInfo.postScriptName size:32.0]; 88 | self.sampleTextView.font = font; 89 | 90 | self.sampleBackgroundView.layer.cornerRadius = 4.0; 91 | 92 | self.installWarningView.hidden = self.fontInfo.isRegistered; 93 | self.installWarningView.layer.cornerRadius = 4.0; 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /xFonts/FontInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // FontInfo.h 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/15/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface FontInfo : NSObject 14 | 15 | @property (class, nonatomic, readonly) NSURL *storageURL; 16 | @property (class, nonatomic, readonly) NSURL *inboxURL; 17 | 18 | - (instancetype)initWithFileURL:(NSURL *)fileURL; 19 | 20 | @property (readonly) NSUInteger hash; 21 | 22 | @property (nonatomic, readonly) NSURL *fileURL; 23 | @property (nonatomic, readonly) NSString *fileName; 24 | @property (nonatomic, readonly) NSString *postScriptName; 25 | @property (nonatomic, readonly, nullable) NSString *displayName; 26 | @property (nonatomic, readonly, nullable) NSString *copyrightName; 27 | @property (nonatomic, readonly, nullable) NSString *descriptionName; 28 | @property (nonatomic, readonly, nullable) NSString *versionName; 29 | @property (nonatomic, readonly, nullable) NSString *styleName; 30 | @property (nonatomic, readonly, nullable) NSString *familyName; 31 | @property (nonatomic, readonly) BOOL isMonospaced; 32 | 33 | @property (nonatomic, readonly) BOOL isRegistered; 34 | @property (nonatomic, readonly) NSInteger numberOfGlyphs; 35 | 36 | - (void)refresh; // reload cached properties (everything derived from fileURL) 37 | 38 | - (BOOL)removeFile; // returns YES if successful 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /xFonts/FontInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // FontInfo.m 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/15/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | @import CoreText; 10 | 11 | #import "FontInfo.h" 12 | 13 | #import "DebugLog.h" 14 | 15 | 16 | @interface FontInfo () 17 | 18 | @property (nonatomic, strong) NSURL *fileURL; 19 | @property (nonatomic, strong) NSString *displayName; 20 | @property (nonatomic, strong) NSString *postScriptName; 21 | @property (nonatomic, strong) NSString *copyrightName; 22 | @property (nonatomic, strong) NSString *descriptionName; 23 | @property (nonatomic, strong) NSString *versionName; 24 | @property (nonatomic, strong) NSString *styleName; 25 | @property (nonatomic, strong) NSString *familyName; 26 | @property (nonatomic, assign) BOOL isMonospaced; 27 | 28 | @property (nonatomic, assign) BOOL isRegistered; 29 | @property (nonatomic, assign) NSInteger numberOfGlyphs; 30 | 31 | @end 32 | 33 | @implementation FontInfo 34 | 35 | + (NSURL *)storageURL 36 | { 37 | NSArray *URLs = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; 38 | NSAssert(URLs != nil && URLs.count > 0, @"Missing document directory"); 39 | return URLs.firstObject; 40 | } 41 | 42 | + (NSURL *)inboxURL 43 | { 44 | return [self.storageURL URLByAppendingPathComponent:@"Inbox"]; 45 | } 46 | 47 | - (instancetype)initWithFileURL:(NSURL *)fileURL 48 | { 49 | if ((self = [self init])) { 50 | _fileURL = fileURL; 51 | 52 | DebugLog(@"%s fileName = %@", __PRETTY_FUNCTION__, self.fileName); 53 | 54 | [self unregisterFont]; 55 | [self extractPropertiesFromFileURL]; 56 | [self registerFont]; 57 | } 58 | 59 | return self; 60 | } 61 | 62 | - (void)dealloc 63 | { 64 | DebugLog(@"%s fileName = %@", __PRETTY_FUNCTION__, self.fileName); 65 | 66 | [self unregisterFont]; 67 | } 68 | 69 | - (NSUInteger)hash 70 | { 71 | return self.fileURL.hash; 72 | } 73 | 74 | #pragma mark Accessors 75 | 76 | - (NSString *)fileName 77 | { 78 | return self.fileURL.lastPathComponent; 79 | } 80 | 81 | #pragma mark - 82 | 83 | - (void)refresh 84 | { 85 | [self unregisterFont]; 86 | [self extractPropertiesFromFileURL]; 87 | [self registerFont]; 88 | } 89 | 90 | - (BOOL)removeFile 91 | { 92 | BOOL result = YES; 93 | 94 | NSError *error; 95 | if (! [NSFileManager.defaultManager removeItemAtURL:self.fileURL error:&error]) { 96 | result = NO; 97 | ReleaseLog(@"%s Could not delete file: %@", __PRETTY_FUNCTION__, [error localizedDescription]); 98 | } 99 | 100 | return result; 101 | } 102 | 103 | #pragma mark - Utility 104 | 105 | - (void)registerFont 106 | { 107 | CFErrorRef errorRef; 108 | if (! CTFontManagerRegisterFontsForURL((CFURLRef)self.fileURL, kCTFontManagerScopeProcess, &errorRef)) { 109 | if (CFErrorGetCode(errorRef) != kCTFontManagerErrorAlreadyRegistered) { // error 105 110 | CFStringRef errorDescription = CFErrorCopyDescription(errorRef); 111 | ReleaseLog(@"%s Failed to register font %@ = %@", __PRETTY_FUNCTION__, self.postScriptName, errorDescription); 112 | CFRelease(errorDescription); 113 | } 114 | } 115 | } 116 | 117 | - (void)unregisterFont 118 | { 119 | CFErrorRef errorRef; 120 | if (! CTFontManagerUnregisterFontsForURL((CFURLRef)self.fileURL, kCTFontManagerScopeProcess, &errorRef)) { 121 | if (CFErrorGetCode(errorRef) != kCTFontManagerErrorNotRegistered) { // error 201 122 | CFStringRef errorDescription = CFErrorCopyDescription(errorRef); 123 | ReleaseLog(@"%s Failed to unregister font %@ = %@", __PRETTY_FUNCTION__, self.postScriptName, errorDescription); 124 | CFRelease(errorDescription); 125 | } 126 | } 127 | } 128 | 129 | - (void)extractPropertiesFromFileURL 130 | { 131 | NSData *fontData = [[NSData alloc] initWithContentsOfURL:self.fileURL]; 132 | if (fontData) { 133 | CGDataProviderRef providerRef = CGDataProviderCreateWithCFData((CFDataRef)fontData); 134 | if (providerRef) { 135 | CGFontRef fontRef = CGFontCreateWithDataProvider(providerRef); 136 | if (fontRef) { 137 | self.numberOfGlyphs = CGFontGetNumberOfGlyphs(fontRef); 138 | 139 | self.postScriptName = CFBridgingRelease(CGFontCopyPostScriptName(fontRef)); 140 | self.displayName = CFBridgingRelease(CGFontCopyFullName(fontRef)); 141 | 142 | // https://stackoverflow.com/questions/53359789/get-meta-info-from-uifont-or-cgfont-ios-swift 143 | CTFontRef textFontRef = CTFontCreateWithGraphicsFont(fontRef, 0, NULL, NULL); 144 | if (textFontRef) { 145 | self.copyrightName = CFBridgingRelease(CTFontCopyName(textFontRef, kCTFontCopyrightNameKey)); 146 | self.descriptionName = CFBridgingRelease(CTFontCopyName(textFontRef, kCTFontDescriptionNameKey)); 147 | self.versionName = CFBridgingRelease(CTFontCopyName(textFontRef, kCTFontVersionNameKey)); 148 | self.styleName = CFBridgingRelease(CTFontCopyName(textFontRef, kCTFontStyleNameKey)); 149 | 150 | self.familyName = CFBridgingRelease(CTFontCopyFamilyName(textFontRef)); 151 | 152 | CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(textFontRef); 153 | self.isMonospaced = (symbolicTraits & kCTFontTraitMonoSpace) != 0; 154 | 155 | CFRelease(textFontRef); 156 | CFRelease(fontRef); 157 | } 158 | else { 159 | ReleaseLog(@"%s no fontRef", __PRETTY_FUNCTION__); 160 | } 161 | 162 | NSArray *availablePostsScriptNames = CFBridgingRelease(CTFontManagerCopyAvailablePostScriptNames()); 163 | self.isRegistered = [availablePostsScriptNames containsObject:self.postScriptName]; 164 | 165 | CFRelease(providerRef); 166 | } 167 | else { 168 | // fallback on file name which _might_ work 169 | self.postScriptName = [self.fileURL.lastPathComponent stringByDeletingPathExtension]; 170 | } 171 | } 172 | else { 173 | ReleaseLog(@"%s no providerRef", __PRETTY_FUNCTION__); 174 | } 175 | } 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /xFonts/HeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderView.h 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/22/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface HeaderView : UIView 14 | 15 | - (void)setFontAddedCount:(NSInteger)addedCount installCount:(NSInteger)installCount; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /xFonts/HeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderView.m 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/22/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import "HeaderView.h" 10 | 11 | @interface HeaderView () 12 | 13 | @property (nonatomic, weak) IBOutlet UILabel *statusLabel; 14 | 15 | @end 16 | 17 | @implementation HeaderView 18 | 19 | - (void)setFontAddedCount:(NSInteger)addedCount installCount:(NSInteger)installCount 20 | { 21 | if (installCount > 0) { 22 | self.statusLabel.text = [NSString stringWithFormat:@"%ld font%s imported, %ld need%s installation", addedCount, (addedCount == 1 ? "" : "s"), installCount, (installCount == 1 ? "s" : "")]; 23 | } 24 | else { 25 | self.statusLabel.text = [NSString stringWithFormat:@"%ld font%s imported", addedCount, (addedCount == 1 ? "" : "s")]; 26 | } 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /xFonts/Help.md: -------------------------------------------------------------------------------- 1 | 2 | **_Welcome to $(PRODUCT_NAME)_** 3 | 4 | If you’ve used fonts on macOS, you’ll be familiar with Font Book and other tools that let you manage your typeface collection. In a similar vein, $(PRODUCT_NAME) is an app that lets you manage your fonts on iOS. You can import your favorite fonts from iCloud Drive or Dropbox, preview the fonts and their metadata, then create an installer for the Settings app. After installation, your fonts can be used by any application. 5 | 6 | The process on iOS is more complicated than putting font files in folder, so if you get stuck, come back to this page for help. 7 | 8 | 9 | **_Overview_** 10 | 11 | The overall approach is to copy font files from either iCloud Drive or Dropbox onto your device using the **Import** button. Once they’re stored locally, you’ll use **Install** to create a mobile device configuration profile. Since you don’t have direct access to the folder where fonts are stored, the _Settings_ app uses the configuration profile to add fonts in a way that makes them available to all apps. 12 | 13 | The fonts you add to $(PRODUCT_NAME) can also be previewed by tapping on the name. When you add a new font to the list, it will be highlighted and you will be prompted to create a new installer: the new faces won’t be available to the system until they are registered with _Settings_. 14 | 15 | 16 | **_Add Your Fonts_** 17 | 18 | iOS can use fonts that are in the Open Type or True Type formats (look for .otf and .ttf file extensions). You can download these fonts directly to your Files using Safari or you can copy them into cloud storage from another device, including a Mac or PC. 19 | 20 | When you tap the **Import** button in the top-left corner of the Fonts list, a file browser will open that lets you navigate to the folder where fonts are stored. You can tap on a single font to import it into $(PRODUCT_NAME) – use _Select_ and _Open_ to get multiple files at once. 21 | 22 | After import, the fonts will appear in the list and can be previewed. If you change your mind about a font, just swipe left to delete it (this won’t affect the original fonts in Files). 23 | 24 | You’re now ready to install the fonts so other apps can use them. 25 | 26 | 27 | **_Install and Download_** 28 | 29 | When you tap the **Install** button in the top-right corner of the Fonts list, a web browser will open with a prominent **Download Fonts** button. After tapping that button you’ll see a warning that “This website is trying to download a configuration profile”. You just put the fonts on your device but now you have to download them. What the heck is going on? 30 | 31 | $(PRODUCT_NAME) created a “mobile device configuration profile” that contains a copy of each font you want to use. The app also started a webserver on your device to give Safari access to that configuration file. When you tap the download link, you’re essentially copying the fonts into your settings. 32 | 33 | You should be careful about downloading configuration profiles. You’re giving a developer the ability to [update your device settings](https://developer.apple.com/business/documentation/Configuration-Profile-Reference.pdf). This includes [embedding identity trackers](https://twitter.com/sandofsky/status/1172200578207772672), adding risky certificates and network configurations, and even changing the apps on your home screen. 34 | 35 | This is the first instance where you need to trust $(PRODUCT_NAME). You should only tap _Allow_ if you have confidence in both the contents and source of the configuration profile. 36 | 37 | To establish this trust, the source code of this app is [available for review](https://github.com/manolosavi/xFonts). $(PRODUCT_NAME) is backed by a company that’s treated customers with respect for [over 20 years](https://iconfactory.com/20years). The [Iconfactory](https://iconfactory.com) contributed their expertise to the xFonts open source project because its inner workings were completely transparent. If this doesn’t give you enough peace of mind, you can build and run the app yourself using Xcode. 38 | 39 | If you do decide to tap _Allow_ you’ll see a dialog that tells you to review the profile in _Settings_ and no other information. Tap _Close_ and you’re ready for the next phase of installation. 40 | 41 | 42 | **_Update Your Settings_** 43 | 44 | Open the _Settings_ app and you’ll see a _Profile Downloaded_ button above the Airplane Mode switch. (If not, navigate to _General_ > _Profiles_ > _Downloaded Profile_ > _$(PRODUCT_NAME) Installation_.) After tapping the button, the first thing you’ll see is “Not Signed” highlighted in red. 45 | 46 | This is another instance where you need to trust the source of the profile and its contents. Since the profile was generated on your device, there’s no risk that it was modified in transit. This also means there’s no way to sign a profile locally without exposing a secret: the private key would have to be in the app’s source code or resources. 47 | 48 | If you tap on _More Details_ you’ll be able to verify that only fonts are included in the profile. The integrity of those fonts can only be determined by reviewing source code or trusting the developer providing the app. Tap the _back button_ and then tap on _Install_ in the upper-right corner. 49 | 50 | Enter your passcode, then you will be warned that the profile is unsigned. Tap _Install_, then tap _Install_ again. 51 | 52 | If you see “Profile Installed” at the top of the screen, congratulations! You now have more choices for text in [iWork](https://www.apple.com/iwork/) or apps like [Tot](https://tot.rocks) that support custom fonts. 53 | 54 | iOS takes some time to get things sorted out so you may not see the fonts immediately. If you’re having problems, try checking again in a few minutes or restarting an app so it re-checks for available fonts. 55 | 56 | 57 | **_Conclusion_** 58 | 59 | Remember that you’ll need to go through the whole installation process again if you add any new type to $(PRODUCT_NAME). Your original profile is replaced after a download, but you still need to authorize the fonts with a passcode. 60 | 61 | If you’d like this process to be easier, [please give Apple some feedback](https://www.apple.com/feedback/iphone.html). 62 | -------------------------------------------------------------------------------- /xFonts/HelpViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HelpViewController.h 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/22/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface HelpViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /xFonts/HelpViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HelpViewController.m 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/22/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import "HelpViewController.h" 10 | 11 | #import "NSAttributedString+Markdown.h" 12 | 13 | #import "DebugLog.h" 14 | 15 | 16 | @interface HelpViewController () 17 | 18 | @property (nonatomic, weak) IBOutlet UITextView *textView; 19 | 20 | @end 21 | 22 | @implementation HelpViewController 23 | 24 | - (void)viewDidLoad 25 | { 26 | [super viewDidLoad]; 27 | 28 | self.textView.linkTextAttributes = @{ NSForegroundColorAttributeName: [UIColor colorNamed:@"appTint"], NSUnderlineStyleAttributeName: @(1) }; 29 | self.textView.textContainerInset = UIEdgeInsetsMake(20, 15, 20, 15); 30 | 31 | [self loadHelpMarkdown]; 32 | 33 | [self.textView scrollRangeToVisible:NSMakeRange(0, 0)]; 34 | } 35 | 36 | /* 37 | #pragma mark - Navigation 38 | 39 | // In a storyboard-based application, you will often want to do a little preparation before navigation 40 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 41 | // Get the new view controller using [segue destinationViewController]. 42 | // Pass the selected object to the new view controller. 43 | } 44 | */ 45 | 46 | - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection 47 | { 48 | DebugLog(@"%s called", __PRETTY_FUNCTION__); 49 | if (previousTraitCollection.preferredContentSizeCategory != self.traitCollection.preferredContentSizeCategory) { 50 | [self loadHelpMarkdown]; 51 | } 52 | } 53 | 54 | #pragma mark - Utility 55 | 56 | - (void)loadHelpMarkdown 57 | { 58 | NSString *productName = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"]; 59 | 60 | NSURL *markdownURL = [NSBundle.mainBundle URLForResource:@"Help" withExtension:@"md"]; 61 | NSData *markdownData = [NSData dataWithContentsOfURL:markdownURL]; 62 | NSString *markdownString = [[NSString alloc] initWithData:markdownData encoding:NSUTF8StringEncoding]; 63 | NSAssert(markdownString != nil, @"Missing help Markdown text"); 64 | 65 | markdownString = [markdownString stringByReplacingOccurrencesOfString:@"$(PRODUCT_NAME)" withString:productName]; 66 | 67 | UIColor *bodyTextColor = UIColor.secondaryLabelColor; 68 | UIColor *emphasisColor = UIColor.labelColor; 69 | UIColor *highlightColor = [UIColor colorNamed:@"appTint"]; 70 | 71 | UIFont *baseFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; 72 | 73 | UIFont *emphasisBaseFont = [UIFont systemFontOfSize:baseFont.pointSize weight:UIFontWeightMedium]; 74 | UIFontDescriptor *emphasisSingleFontDescriptor = [emphasisBaseFont.fontDescriptor fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitItalic | UIFontDescriptorTraitBold)]; 75 | 76 | UIFont *emphasisSingleFont = [UIFont fontWithDescriptor:emphasisSingleFontDescriptor size:baseFont.pointSize]; 77 | UIFont *emphasisDoubleFont = emphasisBaseFont; 78 | UIFont *emphasisBothFont = [UIFont systemFontOfSize:baseFont.pointSize weight:UIFontWeightHeavy]; 79 | 80 | NSDictionary *> *styleAttributes = @{ 81 | MarkdownStyleEmphasisSingle: @{ 82 | NSFontAttributeName: emphasisSingleFont, 83 | NSForegroundColorAttributeName: emphasisColor 84 | }, 85 | MarkdownStyleEmphasisDouble: @{ 86 | NSFontAttributeName: emphasisDoubleFont, 87 | NSForegroundColorAttributeName: emphasisColor 88 | }, 89 | MarkdownStyleEmphasisBoth: @{ 90 | NSFontAttributeName: emphasisBothFont, 91 | NSForegroundColorAttributeName: highlightColor 92 | }, 93 | }; 94 | 95 | NSDictionary *attributes = @{ NSFontAttributeName: baseFont, NSForegroundColorAttributeName: bodyTextColor }; 96 | 97 | NSAttributedString *attributedString = [[NSAttributedString alloc] initWithMarkdownRepresentation:markdownString baseAttributes:attributes styleAttributes:styleAttributes]; 98 | self.textView.attributedText = attributedString; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /xFonts/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | $(PRODUCT_NAME) 9 | CFBundleDocumentTypes 10 | 11 | 12 | CFBundleTypeIconFiles 13 | 14 | CFBundleTypeName 15 | Font 16 | CFBundleTypeRole 17 | None 18 | LSHandlerRank 19 | Owner 20 | LSItemContentTypes 21 | 22 | public.opentype-font 23 | public.truetype-ttf-font 24 | 25 | 26 | 27 | CFBundleExecutable 28 | $(EXECUTABLE_NAME) 29 | CFBundleIdentifier 30 | $(PRODUCT_BUNDLE_IDENTIFIER) 31 | CFBundleInfoDictionaryVersion 32 | 6.0 33 | CFBundleName 34 | $(PRODUCT_NAME) 35 | CFBundlePackageType 36 | APPL 37 | CFBundleShortVersionString 38 | $(MARKETING_VERSION) 39 | CFBundleVersion 40 | 19 41 | ITSAppUsesNonExemptEncryption 42 | 43 | LSRequiresIPhoneOS 44 | 45 | LSSupportsOpeningDocumentsInPlace 46 | 47 | UIFileSharingEnabled 48 | 49 | UILaunchStoryboardName 50 | Launch Screen 51 | UIMainStoryboardFile 52 | Main 53 | UIRequiredDeviceCapabilities 54 | 55 | armv7 56 | 57 | UIStatusBarStyle 58 | UIStatusBarStyleLightContent 59 | UISupportedInterfaceOrientations 60 | 61 | UIInterfaceOrientationPortrait 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | UIInterfaceOrientationPortraitUpsideDown 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /xFonts/MarkdownAttributedString/NSAttributedString+Markdown.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+Markdown.h 3 | // Tot 4 | // 5 | // Created by Craig Hockenberry on 12/14/19. 6 | // Copyright © 2020 The Iconfactory. All rights reserved. 7 | // 8 | /* 9 | Copyright (c) 2020 The Iconfactory, Inc. 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | */ 29 | 30 | #import 31 | 32 | #if TARGET_OS_OSX 33 | #import 34 | #else 35 | #import 36 | #endif 37 | 38 | #define ALLOW_CODE_MARKERS 0 // EXPERIMENTAL - Currently literals aren't escaped and style attributes are baked in (not using styleAttributes). 39 | 40 | NS_ASSUME_NONNULL_BEGIN 41 | 42 | typedef NSString * MarkdownStyleKey NS_EXTENSIBLE_STRING_ENUM; 43 | 44 | extern MarkdownStyleKey MarkdownStyleEmphasisSingle; // attribute dictionary for occurances of _ or * (emphasis, typically an italic font) 45 | extern MarkdownStyleKey MarkdownStyleEmphasisDouble; // attribute dictionary for occurances of __ or ** (strong, typically a bold font) 46 | extern MarkdownStyleKey MarkdownStyleEmphasisBoth; // attribute dictionary for occurances of _ or * within __ or ** (emphasis and strong, typically a bold italic font) 47 | 48 | #if ALLOW_CODE_MARKERS 49 | extern MarkdownStyleKey MarkdownStyleCode; // EXPERIMENTAL - attribute dictionary for occuranges of ` 50 | #endif 51 | 52 | @interface NSAttributedString (Markdown) 53 | 54 | - (instancetype)initWithMarkdownRepresentation:(NSString *)markdownRepresentation attributes:(nonnull NSDictionary *)attributes; 55 | 56 | - (instancetype)initWithMarkdownRepresentation:(NSString *)markdownRepresentation baseAttributes:(nonnull NSDictionary *)baseAttributes styleAttributes:(nullable NSDictionary *> *)styleAttributes; 57 | 58 | @property (nonatomic, readonly) NSString *markdownRepresentation; 59 | 60 | #ifdef TESTING 61 | // for tests, to quickly check the placement of attributes 62 | @property (nonatomic, readonly) NSString *markdownDebug; 63 | #endif 64 | 65 | @end 66 | 67 | NS_ASSUME_NONNULL_END 68 | -------------------------------------------------------------------------------- /xFonts/MarkdownAttributedString/README.md: -------------------------------------------------------------------------------- 1 | # MarkdownAttributedString 2 | 3 | Adding Markdown support to NSAttributedString. 4 | 5 | ## Why? 6 | 7 | Attributed strings, along with CoreText and UI frameworks for iOS and macOS, are a powerful component used in every app. 8 | 9 | Yet making those attributed strings is a pain in the butt. Your choices are: 10 | 11 | * Write a bunch of code to do it manually. 12 | * Use an external text editor to create a resource which is hard to manage (especially with localization). 13 | 14 | Additionally, with the advent of system fonts that can't be used in TextEdit to make the RTF file, there's no avoiding the need for code when you're dealing with text that needs to match the surrounding user interface. 15 | 16 | ## What? 17 | 18 | This project is an Objective-C category that generates rich text by reading Markdown as the source code. It also allows you to write Markdown using attributed strings. The code only processes link and emphasis span elements in Markdown. There is experimental support for code spans. 19 | 20 | There is no support for Markdown block elements such as headers and lists, although care taken to leave those elements alone. In theory it would be possible to achieve something reasonable using paragraph styles, but I'm not convinced there's a need for these elements and the added complexity they would bring. 21 | 22 | The code is written in Objective-C because it is extending code written in the same language. It was a more natural fit. 23 | 24 | Swift is fully supported. You'll find sample apps in both languages for macOS ([AppKit/Objective-C](AppKit/SampleApp)) and iOS ([UIKit/Objective-C](UIKit/SampleApp) and [UIKit/Swift](UIKit/SwiftSampleApp))). 25 | 26 | The macOS sample app also includes a full suite of tests that check the Markdown parser (for both reading and writing). If you want to add additional Markdown support in this code, familiarize yourself with these tests first. This sample app also makes a good test bed and its [view controller's](AppKit/SampleApp/SampleApp/ViewController.m) `-textDidChange` method logs strings that can be used to create tests. 27 | 28 | ## How? 29 | 30 | [Markdown](https://daringfireball.net/projects/markdown/syntax) is a "visual specification" and I was warned by its author that doing this kind of rich text conversion would be "hairy". It was. 31 | 32 | But the end result is worth it. It's incredibly handy to have Markdown as a tool while you're developing apps that use Apple's frameworks. I have used the code for buttons, labels, and text views; the sample apps included in this repository show you how that's done. Here's a quick taste of how you do it in Swift: 33 | 34 | ```swift 35 | let markdownString = "This is a **_simple_ example** that _shows_ **Markdown** usage." 36 | 37 | myLabel.attributedText = NSAttributedString(markdownRepresentation: markdownString, attributes: [.font : UIFont.systemFont(ofSize: 17.0), .foregroundColor: UIColor.systemPurple ]) 38 | ``` 39 | 40 | If you've used HTML to do this in the past, you'll know that it pulls in WebKit, is not particularly fast, and has [thread-safety issues](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/CreatingAttributedStrings.html). This new approach using Markdown has no external dependencies and can be used off the main thread. The only restriction is that the conversion code can only run on [a single thread at one time](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html) because it mutates foundation objects. The code is fast and typically used for operations that are not performed repeatedly, so it's unlikely that you'll have any issues. 41 | 42 | Both producing and consuming Markdown involves a lot of text scanning. When going from Markdown to an attributed string, each type of marker (e.g. \*\* or \_ ) is checked and styling is added incrementally to the string. Going the other direction, each attribute range is checked and markers are emitted accordingly. Of course, once you get into the details of the implementation, you'll realize that it gets more complicated than you'd first expect. The only regular expressions used are to detect URLs and email addresses. So yeah, "hairy". 43 | 44 | One of the potential uses I see for this code is with localization. Putting Markdown into your .strings files will be a lot easier than juggling separate RTF files. Be careful about styles that are only available in Latin languages. A good example is the Japanese phrase これはテストです which can be rendered faithfully in a bold font variant (**これはテストです**) but not italic. _これはテストです_ can be synthesized or substituted from another font, but quality will suffer. In these cases, you're better off using the method with `styleAttributes` to add an underline or some other visual emphasis that doesn't rely on the glyph's structure. 45 | 46 | ## Where? 47 | 48 | [Tot](http://tot.rocks). 49 | 50 | This app was the first place I used this code and when you start using it, you'll understand why. Working in both rich text and Markdown is a seamless experience. As folks have been using this app over the past few months, many weird edge cases have emerged: the parsing and generation of Markdown is robust as a result. 51 | 52 | ## Who? 53 | 54 | This code was written by Craig Hockenberry. If you'd like to show your appreciation, there are several ways to do that: 55 | 56 | * [Get Tot!](http://tot.rocks) - if you love Markdown and text in general, you'll love the app that inspired this code. It's free on macOS and a one-time purchase on iOS. 57 | * [Support our Patreon](https://patreon.com/iconfactory) - you'll be supporting a good cause and get tons of cool stuff in return. 58 | * [Buy our apps](https://iconfactoryapps.com) - the Iconfactory has been making software for over twenty years and we're sure to have something that will appeal to you. 59 | 60 | As with any software, there is plenty of room for improvement. Feel free to send pull requests and file issues. It's likely that I will ignore any issues that don't have a failing test case. 61 | 62 | ## License 63 | 64 | This code uses a MIT License: 65 | 66 | Copyright (c) 2020 The Iconfactory, Inc. 67 | 68 | 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: 69 | 70 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 71 | 72 | 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. 73 | -------------------------------------------------------------------------------- /xFonts/NavigationController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.h 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/17/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NavigationController : UINavigationController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /xFonts/NavigationController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.m 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/17/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import "NavigationController.h" 10 | 11 | @interface NavigationController () 12 | 13 | @end 14 | 15 | @implementation NavigationController 16 | 17 | - (UIStatusBarStyle)preferredStatusBarStyle 18 | { 19 | return UIStatusBarStyleLightContent; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Categories/DDData.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSData (DDData) 4 | 5 | - (NSData *)md5Digest; 6 | 7 | - (NSData *)sha1Digest; 8 | 9 | - (NSString *)hexStringValue; 10 | 11 | - (NSString *)base64Encoded; 12 | - (NSData *)base64Decoded; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Categories/DDData.m: -------------------------------------------------------------------------------- 1 | #import "DDData.h" 2 | #import 3 | 4 | 5 | @implementation NSData (DDData) 6 | 7 | static char encodingTable[64] = { 8 | 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 9 | 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 10 | 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 11 | 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; 12 | 13 | - (NSData *)md5Digest 14 | { 15 | unsigned char result[CC_MD5_DIGEST_LENGTH]; 16 | 17 | CC_MD5([self bytes], (CC_LONG)[self length], result); 18 | return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH]; 19 | } 20 | 21 | - (NSData *)sha1Digest 22 | { 23 | unsigned char result[CC_SHA1_DIGEST_LENGTH]; 24 | 25 | CC_SHA1([self bytes], (CC_LONG)[self length], result); 26 | return [NSData dataWithBytes:result length:CC_SHA1_DIGEST_LENGTH]; 27 | } 28 | 29 | - (NSString *)hexStringValue 30 | { 31 | NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 2)]; 32 | 33 | const unsigned char *dataBuffer = [self bytes]; 34 | int i; 35 | 36 | for (i = 0; i < [self length]; ++i) 37 | { 38 | [stringBuffer appendFormat:@"%02x", (unsigned int)dataBuffer[i]]; 39 | } 40 | 41 | return [stringBuffer copy]; 42 | } 43 | 44 | - (NSString *)base64Encoded 45 | { 46 | const unsigned char *bytes = [self bytes]; 47 | NSMutableString *result = [NSMutableString stringWithCapacity:[self length]]; 48 | unsigned long ixtext = 0; 49 | unsigned long lentext = [self length]; 50 | long ctremaining = 0; 51 | unsigned char inbuf[3], outbuf[4]; 52 | unsigned short i = 0; 53 | unsigned short charsonline = 0, ctcopy = 0; 54 | unsigned long ix = 0; 55 | 56 | while( YES ) 57 | { 58 | ctremaining = lentext - ixtext; 59 | if( ctremaining <= 0 ) break; 60 | 61 | for( i = 0; i < 3; i++ ) { 62 | ix = ixtext + i; 63 | if( ix < lentext ) inbuf[i] = bytes[ix]; 64 | else inbuf [i] = 0; 65 | } 66 | 67 | outbuf [0] = (inbuf [0] & 0xFC) >> 2; 68 | outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4); 69 | outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6); 70 | outbuf [3] = inbuf [2] & 0x3F; 71 | ctcopy = 4; 72 | 73 | switch( ctremaining ) 74 | { 75 | case 1: 76 | ctcopy = 2; 77 | break; 78 | case 2: 79 | ctcopy = 3; 80 | break; 81 | } 82 | 83 | for( i = 0; i < ctcopy; i++ ) 84 | [result appendFormat:@"%c", encodingTable[outbuf[i]]]; 85 | 86 | for( i = ctcopy; i < 4; i++ ) 87 | [result appendString:@"="]; 88 | 89 | ixtext += 3; 90 | charsonline += 4; 91 | } 92 | 93 | return [NSString stringWithString:result]; 94 | } 95 | 96 | - (NSData *)base64Decoded 97 | { 98 | const unsigned char *bytes = [self bytes]; 99 | NSMutableData *result = [NSMutableData dataWithCapacity:[self length]]; 100 | 101 | unsigned long ixtext = 0; 102 | unsigned long lentext = [self length]; 103 | unsigned char ch = 0; 104 | unsigned char inbuf[4] = {0, 0, 0, 0}; 105 | unsigned char outbuf[3] = {0, 0, 0}; 106 | short i = 0, ixinbuf = 0; 107 | BOOL flignore = NO; 108 | BOOL flendtext = NO; 109 | 110 | while( YES ) 111 | { 112 | if( ixtext >= lentext ) break; 113 | ch = bytes[ixtext++]; 114 | flignore = NO; 115 | 116 | if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A'; 117 | else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26; 118 | else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52; 119 | else if( ch == '+' ) ch = 62; 120 | else if( ch == '=' ) flendtext = YES; 121 | else if( ch == '/' ) ch = 63; 122 | else flignore = YES; 123 | 124 | if( ! flignore ) 125 | { 126 | short ctcharsinbuf = 3; 127 | BOOL flbreak = NO; 128 | 129 | if( flendtext ) 130 | { 131 | if( ! ixinbuf ) break; 132 | if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1; 133 | else ctcharsinbuf = 2; 134 | ixinbuf = 3; 135 | flbreak = YES; 136 | } 137 | 138 | inbuf [ixinbuf++] = ch; 139 | 140 | if( ixinbuf == 4 ) 141 | { 142 | ixinbuf = 0; 143 | outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 ); 144 | outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 ); 145 | outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F ); 146 | 147 | for( i = 0; i < ctcharsinbuf; i++ ) 148 | [result appendBytes:&outbuf[i] length:1]; 149 | } 150 | 151 | if( flbreak ) break; 152 | } 153 | } 154 | 155 | return [NSData dataWithData:result]; 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Categories/DDNumber.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface NSNumber (DDNumber) 5 | 6 | + (BOOL)parseString:(NSString *)str intoSInt64:(SInt64 *)pNum; 7 | + (BOOL)parseString:(NSString *)str intoUInt64:(UInt64 *)pNum; 8 | 9 | + (BOOL)parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum; 10 | + (BOOL)parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Categories/DDNumber.m: -------------------------------------------------------------------------------- 1 | #import "DDNumber.h" 2 | 3 | 4 | @implementation NSNumber (DDNumber) 5 | 6 | + (BOOL)parseString:(NSString *)str intoSInt64:(SInt64 *)pNum 7 | { 8 | if(str == nil) 9 | { 10 | *pNum = 0; 11 | return NO; 12 | } 13 | 14 | errno = 0; 15 | 16 | // On both 32-bit and 64-bit machines, long long = 64 bit 17 | 18 | *pNum = strtoll([str UTF8String], NULL, 10); 19 | 20 | if(errno != 0) 21 | return NO; 22 | else 23 | return YES; 24 | } 25 | 26 | + (BOOL)parseString:(NSString *)str intoUInt64:(UInt64 *)pNum 27 | { 28 | if(str == nil) 29 | { 30 | *pNum = 0; 31 | return NO; 32 | } 33 | 34 | errno = 0; 35 | 36 | // On both 32-bit and 64-bit machines, unsigned long long = 64 bit 37 | 38 | *pNum = strtoull([str UTF8String], NULL, 10); 39 | 40 | if(errno != 0) 41 | return NO; 42 | else 43 | return YES; 44 | } 45 | 46 | + (BOOL)parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum 47 | { 48 | if(str == nil) 49 | { 50 | *pNum = 0; 51 | return NO; 52 | } 53 | 54 | errno = 0; 55 | 56 | // On LP64, NSInteger = long = 64 bit 57 | // Otherwise, NSInteger = int = long = 32 bit 58 | 59 | *pNum = strtol([str UTF8String], NULL, 10); 60 | 61 | if(errno != 0) 62 | return NO; 63 | else 64 | return YES; 65 | } 66 | 67 | + (BOOL)parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum 68 | { 69 | if(str == nil) 70 | { 71 | *pNum = 0; 72 | return NO; 73 | } 74 | 75 | errno = 0; 76 | 77 | // On LP64, NSUInteger = unsigned long = 64 bit 78 | // Otherwise, NSUInteger = unsigned int = unsigned long = 32 bit 79 | 80 | *pNum = strtoul([str UTF8String], NULL, 10); 81 | 82 | if(errno != 0) 83 | return NO; 84 | else 85 | return YES; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Categories/DDRange.h: -------------------------------------------------------------------------------- 1 | /** 2 | * DDRange is the functional equivalent of a 64 bit NSRange. 3 | * The HTTP Server is designed to support very large files. 4 | * On 32 bit architectures (ppc, i386) NSRange uses unsigned 32 bit integers. 5 | * This only supports a range of up to 4 gigabytes. 6 | * By defining our own variant, we can support a range up to 16 exabytes. 7 | * 8 | * All effort is given such that DDRange functions EXACTLY the same as NSRange. 9 | **/ 10 | 11 | #import 12 | #import 13 | 14 | @class NSString; 15 | 16 | typedef struct _DDRange { 17 | UInt64 location; 18 | UInt64 length; 19 | } DDRange; 20 | 21 | typedef DDRange *DDRangePointer; 22 | 23 | NS_INLINE DDRange DDMakeRange(UInt64 loc, UInt64 len) { 24 | DDRange r; 25 | r.location = loc; 26 | r.length = len; 27 | return r; 28 | } 29 | 30 | NS_INLINE UInt64 DDMaxRange(DDRange range) { 31 | return (range.location + range.length); 32 | } 33 | 34 | NS_INLINE BOOL DDLocationInRange(UInt64 loc, DDRange range) { 35 | return (loc - range.location < range.length); 36 | } 37 | 38 | NS_INLINE BOOL DDEqualRanges(DDRange range1, DDRange range2) { 39 | return ((range1.location == range2.location) && (range1.length == range2.length)); 40 | } 41 | 42 | FOUNDATION_EXPORT DDRange DDUnionRange(DDRange range1, DDRange range2); 43 | FOUNDATION_EXPORT DDRange DDIntersectionRange(DDRange range1, DDRange range2); 44 | FOUNDATION_EXPORT NSString *DDStringFromRange(DDRange range); 45 | FOUNDATION_EXPORT DDRange DDRangeFromString(NSString *aString); 46 | 47 | NSInteger DDRangeCompare(DDRangePointer pDDRange1, DDRangePointer pDDRange2); 48 | 49 | @interface NSValue (NSValueDDRangeExtensions) 50 | 51 | + (NSValue *)valueWithDDRange:(DDRange)range; 52 | - (DDRange)ddrangeValue; 53 | 54 | - (NSInteger)ddrangeCompare:(NSValue *)ddrangeValue; 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Categories/DDRange.m: -------------------------------------------------------------------------------- 1 | #import "DDRange.h" 2 | #import "DDNumber.h" 3 | 4 | DDRange DDUnionRange(DDRange range1, DDRange range2) 5 | { 6 | DDRange result; 7 | 8 | result.location = MIN(range1.location, range2.location); 9 | result.length = MAX(DDMaxRange(range1), DDMaxRange(range2)) - result.location; 10 | 11 | return result; 12 | } 13 | 14 | DDRange DDIntersectionRange(DDRange range1, DDRange range2) 15 | { 16 | DDRange result; 17 | 18 | if((DDMaxRange(range1) < range2.location) || (DDMaxRange(range2) < range1.location)) 19 | { 20 | return DDMakeRange(0, 0); 21 | } 22 | 23 | result.location = MAX(range1.location, range2.location); 24 | result.length = MIN(DDMaxRange(range1), DDMaxRange(range2)) - result.location; 25 | 26 | return result; 27 | } 28 | 29 | NSString *DDStringFromRange(DDRange range) 30 | { 31 | return [NSString stringWithFormat:@"{%qu, %qu}", range.location, range.length]; 32 | } 33 | 34 | DDRange DDRangeFromString(NSString *aString) 35 | { 36 | DDRange result = DDMakeRange(0, 0); 37 | 38 | // NSRange will ignore '-' characters, but not '+' characters 39 | NSCharacterSet *cset = [NSCharacterSet characterSetWithCharactersInString:@"+0123456789"]; 40 | 41 | NSScanner *scanner = [NSScanner scannerWithString:aString]; 42 | [scanner setCharactersToBeSkipped:[cset invertedSet]]; 43 | 44 | NSString *str1 = nil; 45 | NSString *str2 = nil; 46 | 47 | BOOL found1 = [scanner scanCharactersFromSet:cset intoString:&str1]; 48 | BOOL found2 = [scanner scanCharactersFromSet:cset intoString:&str2]; 49 | 50 | if(found1) [NSNumber parseString:str1 intoUInt64:&result.location]; 51 | if(found2) [NSNumber parseString:str2 intoUInt64:&result.length]; 52 | 53 | return result; 54 | } 55 | 56 | NSInteger DDRangeCompare(DDRangePointer pDDRange1, DDRangePointer pDDRange2) 57 | { 58 | // Comparison basis: 59 | // Which range would you encouter first if you started at zero, and began walking towards infinity. 60 | // If you encouter both ranges at the same time, which range would end first. 61 | 62 | if(pDDRange1->location < pDDRange2->location) 63 | { 64 | return NSOrderedAscending; 65 | } 66 | if(pDDRange1->location > pDDRange2->location) 67 | { 68 | return NSOrderedDescending; 69 | } 70 | if(pDDRange1->length < pDDRange2->length) 71 | { 72 | return NSOrderedAscending; 73 | } 74 | if(pDDRange1->length > pDDRange2->length) 75 | { 76 | return NSOrderedDescending; 77 | } 78 | 79 | return NSOrderedSame; 80 | } 81 | 82 | @implementation NSValue (NSValueDDRangeExtensions) 83 | 84 | + (NSValue *)valueWithDDRange:(DDRange)range 85 | { 86 | return [NSValue valueWithBytes:&range objCType:@encode(DDRange)]; 87 | } 88 | 89 | - (DDRange)ddrangeValue 90 | { 91 | DDRange result; 92 | [self getValue:&result]; 93 | return result; 94 | } 95 | 96 | - (NSInteger)ddrangeCompare:(NSValue *)other 97 | { 98 | DDRange r1 = [self ddrangeValue]; 99 | DDRange r2 = [other ddrangeValue]; 100 | 101 | return DDRangeCompare(&r1, &r2); 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/HTTPAuthenticationRequest.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #if TARGET_OS_IPHONE 4 | // Note: You may need to add the CFNetwork Framework to your project 5 | #import 6 | #endif 7 | 8 | @class HTTPMessage; 9 | 10 | 11 | @interface HTTPAuthenticationRequest : NSObject 12 | { 13 | BOOL isBasic; 14 | BOOL isDigest; 15 | 16 | NSString *base64Credentials; 17 | 18 | NSString *username; 19 | NSString *realm; 20 | NSString *nonce; 21 | NSString *uri; 22 | NSString *qop; 23 | NSString *nc; 24 | NSString *cnonce; 25 | NSString *response; 26 | } 27 | - (id)initWithRequest:(HTTPMessage *)request; 28 | 29 | - (BOOL)isBasic; 30 | - (BOOL)isDigest; 31 | 32 | // Basic 33 | - (NSString *)base64Credentials; 34 | 35 | // Digest 36 | - (NSString *)username; 37 | - (NSString *)realm; 38 | - (NSString *)nonce; 39 | - (NSString *)uri; 40 | - (NSString *)qop; 41 | - (NSString *)nc; 42 | - (NSString *)cnonce; 43 | - (NSString *)response; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/HTTPAuthenticationRequest.m: -------------------------------------------------------------------------------- 1 | #import "HTTPAuthenticationRequest.h" 2 | #import "HTTPMessage.h" 3 | 4 | #if ! __has_feature(objc_arc) 5 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 6 | #endif 7 | 8 | @interface HTTPAuthenticationRequest (PrivateAPI) 9 | - (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header; 10 | - (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header; 11 | @end 12 | 13 | 14 | @implementation HTTPAuthenticationRequest 15 | 16 | - (id)initWithRequest:(HTTPMessage *)request 17 | { 18 | if ((self = [super init])) 19 | { 20 | NSString *authInfo = [request headerField:@"Authorization"]; 21 | 22 | isBasic = NO; 23 | if ([authInfo length] >= 6) 24 | { 25 | isBasic = [[authInfo substringToIndex:6] caseInsensitiveCompare:@"Basic "] == NSOrderedSame; 26 | } 27 | 28 | isDigest = NO; 29 | if ([authInfo length] >= 7) 30 | { 31 | isDigest = [[authInfo substringToIndex:7] caseInsensitiveCompare:@"Digest "] == NSOrderedSame; 32 | } 33 | 34 | if (isBasic) 35 | { 36 | NSMutableString *temp = [[authInfo substringFromIndex:6] mutableCopy]; 37 | CFStringTrimWhitespace((__bridge CFMutableStringRef)temp); 38 | 39 | base64Credentials = [temp copy]; 40 | } 41 | 42 | if (isDigest) 43 | { 44 | username = [self quotedSubHeaderFieldValue:@"username" fromHeaderFieldValue:authInfo]; 45 | realm = [self quotedSubHeaderFieldValue:@"realm" fromHeaderFieldValue:authInfo]; 46 | nonce = [self quotedSubHeaderFieldValue:@"nonce" fromHeaderFieldValue:authInfo]; 47 | uri = [self quotedSubHeaderFieldValue:@"uri" fromHeaderFieldValue:authInfo]; 48 | 49 | // It appears from RFC 2617 that the qop is to be given unquoted 50 | // Tests show that Firefox performs this way, but Safari does not 51 | // Thus we'll attempt to retrieve the value as nonquoted, but we'll verify it doesn't start with a quote 52 | qop = [self nonquotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo]; 53 | if(qop && ([qop characterAtIndex:0] == '"')) 54 | { 55 | qop = [self quotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo]; 56 | } 57 | 58 | nc = [self nonquotedSubHeaderFieldValue:@"nc" fromHeaderFieldValue:authInfo]; 59 | cnonce = [self quotedSubHeaderFieldValue:@"cnonce" fromHeaderFieldValue:authInfo]; 60 | response = [self quotedSubHeaderFieldValue:@"response" fromHeaderFieldValue:authInfo]; 61 | } 62 | } 63 | return self; 64 | } 65 | 66 | 67 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 68 | #pragma mark Accessors: 69 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 70 | 71 | - (BOOL)isBasic { 72 | return isBasic; 73 | } 74 | 75 | - (BOOL)isDigest { 76 | return isDigest; 77 | } 78 | 79 | - (NSString *)base64Credentials { 80 | return base64Credentials; 81 | } 82 | 83 | - (NSString *)username { 84 | return username; 85 | } 86 | 87 | - (NSString *)realm { 88 | return realm; 89 | } 90 | 91 | - (NSString *)nonce { 92 | return nonce; 93 | } 94 | 95 | - (NSString *)uri { 96 | return uri; 97 | } 98 | 99 | - (NSString *)qop { 100 | return qop; 101 | } 102 | 103 | - (NSString *)nc { 104 | return nc; 105 | } 106 | 107 | - (NSString *)cnonce { 108 | return cnonce; 109 | } 110 | 111 | - (NSString *)response { 112 | return response; 113 | } 114 | 115 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 116 | #pragma mark Private API: 117 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 118 | 119 | /** 120 | * Retrieves a "Sub Header Field Value" from a given header field value. 121 | * The sub header field is expected to be quoted. 122 | * 123 | * In the following header field: 124 | * Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939" 125 | * The sub header field titled 'username' is quoted, and this method would return the value @"Mufasa". 126 | **/ 127 | - (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header 128 | { 129 | NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=\"", param]]; 130 | if(startRange.location == NSNotFound) 131 | { 132 | // The param was not found anywhere in the header 133 | return nil; 134 | } 135 | 136 | NSUInteger postStartRangeLocation = startRange.location + startRange.length; 137 | NSUInteger postStartRangeLength = [header length] - postStartRangeLocation; 138 | NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength); 139 | 140 | NSRange endRange = [header rangeOfString:@"\"" options:0 range:postStartRange]; 141 | if(endRange.location == NSNotFound) 142 | { 143 | // The ending double-quote was not found anywhere in the header 144 | return nil; 145 | } 146 | 147 | NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation); 148 | return [header substringWithRange:subHeaderRange]; 149 | } 150 | 151 | /** 152 | * Retrieves a "Sub Header Field Value" from a given header field value. 153 | * The sub header field is expected to not be quoted. 154 | * 155 | * In the following header field: 156 | * Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939" 157 | * The sub header field titled 'qop' is nonquoted, and this method would return the value @"auth". 158 | **/ 159 | - (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header 160 | { 161 | NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=", param]]; 162 | if(startRange.location == NSNotFound) 163 | { 164 | // The param was not found anywhere in the header 165 | return nil; 166 | } 167 | 168 | NSUInteger postStartRangeLocation = startRange.location + startRange.length; 169 | NSUInteger postStartRangeLength = [header length] - postStartRangeLocation; 170 | NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength); 171 | 172 | NSRange endRange = [header rangeOfString:@"," options:0 range:postStartRange]; 173 | if(endRange.location == NSNotFound) 174 | { 175 | // The ending comma was not found anywhere in the header 176 | // However, if the nonquoted param is at the end of the string, there would be no comma 177 | // This is only possible if there are no spaces anywhere 178 | NSRange endRange2 = [header rangeOfString:@" " options:0 range:postStartRange]; 179 | if(endRange2.location != NSNotFound) 180 | { 181 | return nil; 182 | } 183 | else 184 | { 185 | return [header substringWithRange:postStartRange]; 186 | } 187 | } 188 | else 189 | { 190 | NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation); 191 | return [header substringWithRange:subHeaderRange]; 192 | } 193 | } 194 | 195 | @end 196 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/HTTPConnection.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class GCDAsyncSocket; 4 | @class HTTPMessage; 5 | @class HTTPServer; 6 | @class WebSocket; 7 | @protocol HTTPResponse; 8 | 9 | 10 | #define HTTPConnectionDidDieNotification @"HTTPConnectionDidDie" 11 | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 | #pragma mark - 14 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 15 | 16 | @interface HTTPConfig : NSObject 17 | { 18 | HTTPServer __unsafe_unretained *server; 19 | NSString __strong *documentRoot; 20 | dispatch_queue_t queue; 21 | } 22 | 23 | - (id)initWithServer:(HTTPServer *)server documentRoot:(NSString *)documentRoot; 24 | - (id)initWithServer:(HTTPServer *)server documentRoot:(NSString *)documentRoot queue:(dispatch_queue_t)q; 25 | 26 | @property (nonatomic, unsafe_unretained, readonly) HTTPServer *server; 27 | @property (nonatomic, strong, readonly) NSString *documentRoot; 28 | @property (nonatomic, readonly) dispatch_queue_t queue; 29 | 30 | @end 31 | 32 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 33 | #pragma mark - 34 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 35 | 36 | @interface HTTPConnection : NSObject 37 | { 38 | dispatch_queue_t connectionQueue; 39 | GCDAsyncSocket *asyncSocket; 40 | HTTPConfig *config; 41 | 42 | BOOL started; 43 | 44 | HTTPMessage *request; 45 | unsigned int numHeaderLines; 46 | 47 | BOOL sentResponseHeaders; 48 | 49 | NSString *nonce; 50 | long lastNC; 51 | 52 | NSObject *httpResponse; 53 | 54 | NSMutableArray *ranges; 55 | NSMutableArray *ranges_headers; 56 | NSString *ranges_boundry; 57 | int rangeIndex; 58 | 59 | UInt64 requestContentLength; 60 | UInt64 requestContentLengthReceived; 61 | UInt64 requestChunkSize; 62 | UInt64 requestChunkSizeReceived; 63 | 64 | NSMutableArray *responseDataSizes; 65 | } 66 | 67 | - (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig *)aConfig; 68 | 69 | - (void)start; 70 | - (void)stop; 71 | 72 | - (void)startConnection; 73 | 74 | - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path; 75 | - (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path; 76 | 77 | - (BOOL)isSecureServer; 78 | - (NSArray *)sslIdentityAndCertificates; 79 | 80 | - (BOOL)isPasswordProtected:(NSString *)path; 81 | - (BOOL)useDigestAccessAuthentication; 82 | - (NSString *)realm; 83 | - (NSString *)passwordForUser:(NSString *)username; 84 | 85 | - (NSDictionary *)parseParams:(NSString *)query; 86 | - (NSDictionary *)parseGetParams; 87 | 88 | - (NSString *)requestURI; 89 | 90 | - (NSArray *)directoryIndexFileNames; 91 | - (NSString *)filePathForURI:(NSString *)path; 92 | - (NSString *)filePathForURI:(NSString *)path allowDirectory:(BOOL)allowDirectory; 93 | - (NSObject *)httpResponseForMethod:(NSString *)method URI:(NSString *)path; 94 | - (WebSocket *)webSocketForURI:(NSString *)path; 95 | 96 | - (void)prepareForBodyWithSize:(UInt64)contentLength; 97 | - (void)processBodyData:(NSData *)postDataChunk; 98 | - (void)finishBody; 99 | 100 | - (void)handleVersionNotSupported:(NSString *)version; 101 | - (void)handleAuthenticationFailed; 102 | - (void)handleResourceNotFound; 103 | - (void)handleInvalidRequest:(NSData *)data; 104 | - (void)handleUnknownMethod:(NSString *)method; 105 | 106 | - (NSData *)preprocessResponse:(HTTPMessage *)response; 107 | - (NSData *)preprocessErrorResponse:(HTTPMessage *)response; 108 | 109 | - (void)finishResponse; 110 | 111 | - (BOOL)shouldDie; 112 | - (void)die; 113 | 114 | @end 115 | 116 | @interface HTTPConnection (AsynchronousHTTPResponse) 117 | - (void)responseHasAvailableData:(NSObject *)sender; 118 | - (void)responseDidAbort:(NSObject *)sender; 119 | @end 120 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/HTTPLogging.h: -------------------------------------------------------------------------------- 1 | /** 2 | * In order to provide fast and flexible logging, this project uses Cocoa Lumberjack. 3 | * 4 | * The Google Code page has a wealth of documentation if you have any questions. 5 | * https://github.com/robbiehanson/CocoaLumberjack 6 | * 7 | * Here's what you need to know concerning how logging is setup for CocoaHTTPServer: 8 | * 9 | * There are 4 log levels: 10 | * - Error 11 | * - Warning 12 | * - Info 13 | * - Verbose 14 | * 15 | * In addition to this, there is a Trace flag that can be enabled. 16 | * When tracing is enabled, it spits out the methods that are being called. 17 | * 18 | * Please note that tracing is separate from the log levels. 19 | * For example, one could set the log level to warning, and enable tracing. 20 | * 21 | * All logging is asynchronous, except errors. 22 | * To use logging within your own custom files, follow the steps below. 23 | * 24 | * Step 1: 25 | * Import this header in your implementation file: 26 | * 27 | * #import "HTTPLogging.h" 28 | * 29 | * Step 2: 30 | * Define your logging level in your implementation file: 31 | * 32 | * // Log levels: off, error, warn, info, verbose 33 | * static const int httpLogLevel = HTTP_LOG_LEVEL_VERBOSE; 34 | * 35 | * If you wish to enable tracing, you could do something like this: 36 | * 37 | * // Debug levels: off, error, warn, info, verbose 38 | * static const int httpLogLevel = HTTP_LOG_LEVEL_INFO | HTTP_LOG_FLAG_TRACE; 39 | * 40 | * Step 3: 41 | * Replace your NSLog statements with HTTPLog statements according to the severity of the message. 42 | * 43 | * NSLog(@"Fatal error, no dohickey found!"); -> HTTPLogError(@"Fatal error, no dohickey found!"); 44 | * 45 | * HTTPLog works exactly the same as NSLog. 46 | * This means you can pass it multiple variables just like NSLog. 47 | **/ 48 | 49 | #import "DDLog.h" 50 | 51 | // Define logging context for every log message coming from the HTTP server. 52 | // The logging context can be extracted from the DDLogMessage from within the logging framework, 53 | // which gives loggers, formatters, and filters the ability to optionally process them differently. 54 | 55 | #define HTTP_LOG_CONTEXT 80 56 | 57 | // Configure log levels. 58 | 59 | #define HTTP_LOG_FLAG_ERROR (1 << 0) // 0...00001 60 | #define HTTP_LOG_FLAG_WARN (1 << 1) // 0...00010 61 | #define HTTP_LOG_FLAG_INFO (1 << 2) // 0...00100 62 | #define HTTP_LOG_FLAG_VERBOSE (1 << 3) // 0...01000 63 | 64 | #define HTTP_LOG_LEVEL_OFF 0 // 0...00000 65 | #define HTTP_LOG_LEVEL_ERROR (HTTP_LOG_LEVEL_OFF | HTTP_LOG_FLAG_ERROR) // 0...00001 66 | #define HTTP_LOG_LEVEL_WARN (HTTP_LOG_LEVEL_ERROR | HTTP_LOG_FLAG_WARN) // 0...00011 67 | #define HTTP_LOG_LEVEL_INFO (HTTP_LOG_LEVEL_WARN | HTTP_LOG_FLAG_INFO) // 0...00111 68 | #define HTTP_LOG_LEVEL_VERBOSE (HTTP_LOG_LEVEL_INFO | HTTP_LOG_FLAG_VERBOSE) // 0...01111 69 | 70 | // Setup fine grained logging. 71 | // The first 4 bits are being used by the standard log levels (0 - 3) 72 | // 73 | // We're going to add tracing, but NOT as a log level. 74 | // Tracing can be turned on and off independently of log level. 75 | 76 | #define HTTP_LOG_FLAG_TRACE (1 << 4) // 0...10000 77 | 78 | // Setup the usual boolean macros. 79 | 80 | #define HTTP_LOG_ERROR (httpLogLevel & HTTP_LOG_FLAG_ERROR) 81 | #define HTTP_LOG_WARN (httpLogLevel & HTTP_LOG_FLAG_WARN) 82 | #define HTTP_LOG_INFO (httpLogLevel & HTTP_LOG_FLAG_INFO) 83 | #define HTTP_LOG_VERBOSE (httpLogLevel & HTTP_LOG_FLAG_VERBOSE) 84 | #define HTTP_LOG_TRACE (httpLogLevel & HTTP_LOG_FLAG_TRACE) 85 | 86 | // Configure asynchronous logging. 87 | // We follow the default configuration, 88 | // but we reserve a special macro to easily disable asynchronous logging for debugging purposes. 89 | 90 | #define HTTP_LOG_ASYNC_ENABLED YES 91 | 92 | #define HTTP_LOG_ASYNC_ERROR ( NO && HTTP_LOG_ASYNC_ENABLED) 93 | #define HTTP_LOG_ASYNC_WARN (YES && HTTP_LOG_ASYNC_ENABLED) 94 | #define HTTP_LOG_ASYNC_INFO (YES && HTTP_LOG_ASYNC_ENABLED) 95 | #define HTTP_LOG_ASYNC_VERBOSE (YES && HTTP_LOG_ASYNC_ENABLED) 96 | #define HTTP_LOG_ASYNC_TRACE (YES && HTTP_LOG_ASYNC_ENABLED) 97 | 98 | // Define logging primitives. 99 | 100 | #define HTTPLogError(frmt, ...) LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_ERROR, httpLogLevel, HTTP_LOG_FLAG_ERROR, \ 101 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 102 | 103 | #define HTTPLogWarn(frmt, ...) LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_WARN, httpLogLevel, HTTP_LOG_FLAG_WARN, \ 104 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 105 | 106 | #define HTTPLogInfo(frmt, ...) LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_INFO, httpLogLevel, HTTP_LOG_FLAG_INFO, \ 107 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 108 | 109 | #define HTTPLogVerbose(frmt, ...) LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_VERBOSE, httpLogLevel, HTTP_LOG_FLAG_VERBOSE, \ 110 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 111 | 112 | #define HTTPLogTrace() LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_TRACE, httpLogLevel, HTTP_LOG_FLAG_TRACE, \ 113 | HTTP_LOG_CONTEXT, @"%@[%p]: %@", THIS_FILE, self, THIS_METHOD) 114 | 115 | #define HTTPLogTrace2(frmt, ...) LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_TRACE, httpLogLevel, HTTP_LOG_FLAG_TRACE, \ 116 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 117 | 118 | 119 | #define HTTPLogCError(frmt, ...) LOG_C_MAYBE(HTTP_LOG_ASYNC_ERROR, httpLogLevel, HTTP_LOG_FLAG_ERROR, \ 120 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 121 | 122 | #define HTTPLogCWarn(frmt, ...) LOG_C_MAYBE(HTTP_LOG_ASYNC_WARN, httpLogLevel, HTTP_LOG_FLAG_WARN, \ 123 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 124 | 125 | #define HTTPLogCInfo(frmt, ...) LOG_C_MAYBE(HTTP_LOG_ASYNC_INFO, httpLogLevel, HTTP_LOG_FLAG_INFO, \ 126 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 127 | 128 | #define HTTPLogCVerbose(frmt, ...) LOG_C_MAYBE(HTTP_LOG_ASYNC_VERBOSE, httpLogLevel, HTTP_LOG_FLAG_VERBOSE, \ 129 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 130 | 131 | #define HTTPLogCTrace() LOG_C_MAYBE(HTTP_LOG_ASYNC_TRACE, httpLogLevel, HTTP_LOG_FLAG_TRACE, \ 132 | HTTP_LOG_CONTEXT, @"%@[%p]: %@", THIS_FILE, self, __FUNCTION__) 133 | 134 | #define HTTPLogCTrace2(frmt, ...) LOG_C_MAYBE(HTTP_LOG_ASYNC_TRACE, httpLogLevel, HTTP_LOG_FLAG_TRACE, \ 135 | HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__) 136 | 137 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/HTTPMessage.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The HTTPMessage class is a simple Objective-C wrapper around Apple's CFHTTPMessage class. 3 | **/ 4 | 5 | #import 6 | 7 | #if TARGET_OS_IPHONE 8 | // Note: You may need to add the CFNetwork Framework to your project 9 | #import 10 | #endif 11 | 12 | #define HTTPVersion1_0 ((NSString *)kCFHTTPVersion1_0) 13 | #define HTTPVersion1_1 ((NSString *)kCFHTTPVersion1_1) 14 | 15 | 16 | @interface HTTPMessage : NSObject 17 | { 18 | CFHTTPMessageRef message; 19 | } 20 | 21 | - (id)initEmptyRequest; 22 | 23 | - (id)initRequestWithMethod:(NSString *)method URL:(NSURL *)url version:(NSString *)version; 24 | 25 | - (id)initResponseWithStatusCode:(NSInteger)code description:(NSString *)description version:(NSString *)version; 26 | 27 | - (BOOL)appendData:(NSData *)data; 28 | 29 | - (BOOL)isHeaderComplete; 30 | 31 | - (NSString *)version; 32 | 33 | - (NSString *)method; 34 | - (NSURL *)url; 35 | 36 | - (NSInteger)statusCode; 37 | 38 | - (NSDictionary *)allHeaderFields; 39 | - (NSString *)headerField:(NSString *)headerField; 40 | 41 | - (void)setHeaderField:(NSString *)headerField value:(NSString *)headerFieldValue; 42 | 43 | - (NSData *)messageData; 44 | 45 | - (NSData *)body; 46 | - (void)setBody:(NSData *)body; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/HTTPMessage.m: -------------------------------------------------------------------------------- 1 | #import "HTTPMessage.h" 2 | 3 | #if ! __has_feature(objc_arc) 4 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 5 | #endif 6 | 7 | 8 | @implementation HTTPMessage 9 | 10 | - (id)initEmptyRequest 11 | { 12 | if ((self = [super init])) 13 | { 14 | message = CFHTTPMessageCreateEmpty(NULL, YES); 15 | } 16 | return self; 17 | } 18 | 19 | - (id)initRequestWithMethod:(NSString *)method URL:(NSURL *)url version:(NSString *)version 20 | { 21 | if ((self = [super init])) 22 | { 23 | message = CFHTTPMessageCreateRequest(NULL, 24 | (__bridge CFStringRef)method, 25 | (__bridge CFURLRef)url, 26 | (__bridge CFStringRef)version); 27 | } 28 | return self; 29 | } 30 | 31 | - (id)initResponseWithStatusCode:(NSInteger)code description:(NSString *)description version:(NSString *)version 32 | { 33 | if ((self = [super init])) 34 | { 35 | message = CFHTTPMessageCreateResponse(NULL, 36 | (CFIndex)code, 37 | (__bridge CFStringRef)description, 38 | (__bridge CFStringRef)version); 39 | } 40 | return self; 41 | } 42 | 43 | - (void)dealloc 44 | { 45 | if (message) 46 | { 47 | CFRelease(message); 48 | } 49 | } 50 | 51 | - (BOOL)appendData:(NSData *)data 52 | { 53 | return CFHTTPMessageAppendBytes(message, [data bytes], [data length]); 54 | } 55 | 56 | - (BOOL)isHeaderComplete 57 | { 58 | return CFHTTPMessageIsHeaderComplete(message); 59 | } 60 | 61 | - (NSString *)version 62 | { 63 | return (__bridge_transfer NSString *)CFHTTPMessageCopyVersion(message); 64 | } 65 | 66 | - (NSString *)method 67 | { 68 | return (__bridge_transfer NSString *)CFHTTPMessageCopyRequestMethod(message); 69 | } 70 | 71 | - (NSURL *)url 72 | { 73 | return (__bridge_transfer NSURL *)CFHTTPMessageCopyRequestURL(message); 74 | } 75 | 76 | - (NSInteger)statusCode 77 | { 78 | return (NSInteger)CFHTTPMessageGetResponseStatusCode(message); 79 | } 80 | 81 | - (NSDictionary *)allHeaderFields 82 | { 83 | return (__bridge_transfer NSDictionary *)CFHTTPMessageCopyAllHeaderFields(message); 84 | } 85 | 86 | - (NSString *)headerField:(NSString *)headerField 87 | { 88 | return (__bridge_transfer NSString *)CFHTTPMessageCopyHeaderFieldValue(message, (__bridge CFStringRef)headerField); 89 | } 90 | 91 | - (void)setHeaderField:(NSString *)headerField value:(NSString *)headerFieldValue 92 | { 93 | CFHTTPMessageSetHeaderFieldValue(message, 94 | (__bridge CFStringRef)headerField, 95 | (__bridge CFStringRef)headerFieldValue); 96 | } 97 | 98 | - (NSData *)messageData 99 | { 100 | return (__bridge_transfer NSData *)CFHTTPMessageCopySerializedMessage(message); 101 | } 102 | 103 | - (NSData *)body 104 | { 105 | return (__bridge_transfer NSData *)CFHTTPMessageCopyBody(message); 106 | } 107 | 108 | - (void)setBody:(NSData *)body 109 | { 110 | CFHTTPMessageSetBody(message, (__bridge CFDataRef)body); 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/HTTPServer.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class GCDAsyncSocket; 4 | @class WebSocket; 5 | 6 | #if TARGET_OS_IPHONE 7 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 // iPhone 4.0 8 | #define IMPLEMENTED_PROTOCOLS 9 | #else 10 | #define IMPLEMENTED_PROTOCOLS 11 | #endif 12 | #else 13 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 // Mac OS X 10.6 14 | #define IMPLEMENTED_PROTOCOLS 15 | #else 16 | #define IMPLEMENTED_PROTOCOLS 17 | #endif 18 | #endif 19 | 20 | 21 | @interface HTTPServer : NSObject IMPLEMENTED_PROTOCOLS 22 | { 23 | // Underlying asynchronous TCP/IP socket 24 | GCDAsyncSocket *asyncSocket; 25 | 26 | // Dispatch queues 27 | dispatch_queue_t serverQueue; 28 | dispatch_queue_t connectionQueue; 29 | void *IsOnServerQueueKey; 30 | void *IsOnConnectionQueueKey; 31 | 32 | // HTTP server configuration 33 | NSString *documentRoot; 34 | Class connectionClass; 35 | NSString *interface; 36 | UInt16 port; 37 | 38 | // NSNetService and related variables 39 | NSNetService *netService; 40 | NSString *domain; 41 | NSString *type; 42 | NSString *name; 43 | NSString *publishedName; 44 | NSDictionary *txtRecordDictionary; 45 | 46 | // Connection management 47 | NSMutableArray *connections; 48 | NSMutableArray *webSockets; 49 | NSLock *connectionsLock; 50 | NSLock *webSocketsLock; 51 | 52 | BOOL isRunning; 53 | } 54 | 55 | /** 56 | * Specifies the document root to serve files from. 57 | * For example, if you set this to "/Users//Sites", 58 | * then it will serve files out of the local Sites directory (including subdirectories). 59 | * 60 | * The default value is nil. 61 | * The default server configuration will not serve any files until this is set. 62 | * 63 | * If you change the documentRoot while the server is running, 64 | * the change will affect future incoming http connections. 65 | **/ 66 | - (NSString *)documentRoot; 67 | - (void)setDocumentRoot:(NSString *)value; 68 | 69 | /** 70 | * The connection class is the class used to handle incoming HTTP connections. 71 | * 72 | * The default value is [HTTPConnection class]. 73 | * You can override HTTPConnection, and then set this to [MyHTTPConnection class]. 74 | * 75 | * If you change the connectionClass while the server is running, 76 | * the change will affect future incoming http connections. 77 | **/ 78 | - (Class)connectionClass; 79 | - (void)setConnectionClass:(Class)value; 80 | 81 | /** 82 | * Set what interface you'd like the server to listen on. 83 | * By default this is nil, which causes the server to listen on all available interfaces like en1, wifi etc. 84 | * 85 | * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34"). 86 | * You may also use the special strings "localhost" or "loopback" to specify that 87 | * the socket only accept connections from the local machine. 88 | **/ 89 | - (NSString *)interface; 90 | - (void)setInterface:(NSString *)value; 91 | 92 | /** 93 | * The port number to run the HTTP server on. 94 | * 95 | * The default port number is zero, meaning the server will automatically use any available port. 96 | * This is the recommended port value, as it avoids possible port conflicts with other applications. 97 | * Technologies such as Bonjour can be used to allow other applications to automatically discover the port number. 98 | * 99 | * Note: As is common on most OS's, you need root privledges to bind to port numbers below 1024. 100 | * 101 | * You can change the port property while the server is running, but it won't affect the running server. 102 | * To actually change the port the server is listening for connections on you'll need to restart the server. 103 | * 104 | * The listeningPort method will always return the port number the running server is listening for connections on. 105 | * If the server is not running this method returns 0. 106 | **/ 107 | - (UInt16)port; 108 | - (UInt16)listeningPort; 109 | - (void)setPort:(UInt16)value; 110 | 111 | /** 112 | * Bonjour domain for publishing the service. 113 | * The default value is "local.". 114 | * 115 | * Note: Bonjour publishing requires you set a type. 116 | * 117 | * If you change the domain property after the bonjour service has already been published (server already started), 118 | * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service. 119 | **/ 120 | - (NSString *)domain; 121 | - (void)setDomain:(NSString *)value; 122 | 123 | /** 124 | * Bonjour name for publishing the service. 125 | * The default value is "". 126 | * 127 | * If using an empty string ("") for the service name when registering, 128 | * the system will automatically use the "Computer Name". 129 | * Using an empty string will also handle name conflicts 130 | * by automatically appending a digit to the end of the name. 131 | * 132 | * Note: Bonjour publishing requires you set a type. 133 | * 134 | * If you change the name after the bonjour service has already been published (server already started), 135 | * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service. 136 | * 137 | * The publishedName method will always return the actual name that was published via the bonjour service. 138 | * If the service is not running this method returns nil. 139 | **/ 140 | - (NSString *)name; 141 | - (NSString *)publishedName; 142 | - (void)setName:(NSString *)value; 143 | 144 | /** 145 | * Bonjour type for publishing the service. 146 | * The default value is nil. 147 | * The service will not be published via bonjour unless the type is set. 148 | * 149 | * If you wish to publish the service as a traditional HTTP server, you should set the type to be "_http._tcp.". 150 | * 151 | * If you change the type after the bonjour service has already been published (server already started), 152 | * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service. 153 | **/ 154 | - (NSString *)type; 155 | - (void)setType:(NSString *)value; 156 | 157 | /** 158 | * Republishes the service via bonjour if the server is running. 159 | * If the service was not previously published, this method will publish it (if the server is running). 160 | **/ 161 | - (void)republishBonjour; 162 | 163 | /** 164 | * 165 | **/ 166 | - (NSDictionary *)TXTRecordDictionary; 167 | - (void)setTXTRecordDictionary:(NSDictionary *)dict; 168 | 169 | /** 170 | * Attempts to starts the server on the configured port, interface, etc. 171 | * 172 | * If an error occurs, this method returns NO and sets the errPtr (if given). 173 | * Otherwise returns YES on success. 174 | * 175 | * Some examples of errors that might occur: 176 | * - You specified the server listen on a port which is already in use by another application. 177 | * - You specified the server listen on a port number below 1024, which requires root priviledges. 178 | * 179 | * Code Example: 180 | * 181 | * NSError *err = nil; 182 | * if (![httpServer start:&err]) 183 | * { 184 | * NSLog(@"Error starting http server: %@", err); 185 | * } 186 | **/ 187 | - (BOOL)start:(NSError **)errPtr; 188 | 189 | /** 190 | * Stops the server, preventing it from accepting any new connections. 191 | * You may specify whether or not you want to close the existing client connections. 192 | * 193 | * The default stop method (with no arguments) will close any existing connections. (It invokes [self stop:NO]) 194 | **/ 195 | - (void)stop; 196 | - (void)stop:(BOOL)keepExistingConnections; 197 | 198 | - (BOOL)isRunning; 199 | 200 | - (void)addWebSocket:(WebSocket *)ws; 201 | 202 | - (NSUInteger)numberOfHTTPConnections; 203 | - (NSUInteger)numberOfWebSocketConnections; 204 | 205 | @end 206 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.h: -------------------------------------------------------------------------------- 1 | 2 | #import "MultipartMessageHeader.h" 3 | 4 | /* 5 | Part one: http://tools.ietf.org/html/rfc2045 (Format of Internet Message Bodies) 6 | Part two: http://tools.ietf.org/html/rfc2046 (Media Types) 7 | Part three: http://tools.ietf.org/html/rfc2047 (Message Header Extensions for Non-ASCII Text) 8 | Part four: http://tools.ietf.org/html/rfc4289 (Registration Procedures) 9 | Part five: http://tools.ietf.org/html/rfc2049 (Conformance Criteria and Examples) 10 | 11 | Internet message format: http://tools.ietf.org/html/rfc2822 12 | 13 | Multipart/form-data http://tools.ietf.org/html/rfc2388 14 | */ 15 | 16 | @class MultipartFormDataParser; 17 | 18 | //----------------------------------------------------------------- 19 | // protocol MultipartFormDataParser 20 | //----------------------------------------------------------------- 21 | 22 | @protocol MultipartFormDataParserDelegate 23 | @optional 24 | - (void) processContent:(NSData*) data WithHeader:(MultipartMessageHeader*) header; 25 | - (void) processEndOfPartWithHeader:(MultipartMessageHeader*) header; 26 | - (void) processPreambleData:(NSData*) data; 27 | - (void) processEpilogueData:(NSData*) data; 28 | - (void) processStartOfPartWithHeader:(MultipartMessageHeader*) header; 29 | @end 30 | 31 | //----------------------------------------------------------------- 32 | // interface MultipartFormDataParser 33 | //----------------------------------------------------------------- 34 | 35 | @interface MultipartFormDataParser : NSObject { 36 | NSMutableData* pendingData; 37 | NSData* boundaryData; 38 | MultipartMessageHeader* currentHeader; 39 | 40 | BOOL waitingForCRLF; 41 | BOOL reachedEpilogue; 42 | BOOL processedPreamble; 43 | BOOL checkForContentEnd; 44 | 45 | #if __has_feature(objc_arc_weak) 46 | __weak id delegate; 47 | #else 48 | __unsafe_unretained id delegate; 49 | #endif 50 | int currentEncoding; 51 | NSStringEncoding formEncoding; 52 | } 53 | 54 | - (BOOL) appendData:(NSData*) data; 55 | 56 | - (id) initWithBoundary:(NSString*) boundary formEncoding:(NSStringEncoding) formEncoding; 57 | 58 | #if __has_feature(objc_arc_weak) 59 | @property(weak, readwrite) id delegate; 60 | #else 61 | @property(unsafe_unretained, readwrite) id delegate; 62 | #endif 63 | @property(readwrite) NSStringEncoding formEncoding; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultipartMessagePart.h 3 | // HttpServer 4 | // 5 | // Created by Валерий Гаврилов on 29.03.12. 6 | // Copyright (c) 2012 LLC "Online Publishing Partners" (onlinepp.ru). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | //----------------------------------------------------------------- 13 | // interface MultipartMessageHeader 14 | //----------------------------------------------------------------- 15 | enum { 16 | contentTransferEncoding_unknown, 17 | contentTransferEncoding_7bit, 18 | contentTransferEncoding_8bit, 19 | contentTransferEncoding_binary, 20 | contentTransferEncoding_base64, 21 | contentTransferEncoding_quotedPrintable, 22 | }; 23 | 24 | @interface MultipartMessageHeader : NSObject { 25 | NSMutableDictionary* fields; 26 | int encoding; 27 | NSString* contentDispositionName; 28 | } 29 | @property (strong,readonly) NSDictionary* fields; 30 | @property (readonly) int encoding; 31 | 32 | - (id) initWithData:(NSData*) data formEncoding:(NSStringEncoding) encoding; 33 | @end 34 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultipartMessagePart.m 3 | // HttpServer 4 | // 5 | // Created by Валерий Гаврилов on 29.03.12. 6 | // Copyright (c) 2012 LLC "Online Publishing Partners" (onlinepp.ru). All rights reserved. 7 | 8 | #import "MultipartMessageHeader.h" 9 | #import "MultipartMessageHeaderField.h" 10 | 11 | #import "HTTPLogging.h" 12 | 13 | //----------------------------------------------------------------- 14 | #pragma mark log level 15 | 16 | #ifdef DEBUG 17 | static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; 18 | #else 19 | static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; 20 | #endif 21 | 22 | //----------------------------------------------------------------- 23 | // implementation MultipartMessageHeader 24 | //----------------------------------------------------------------- 25 | 26 | 27 | @implementation MultipartMessageHeader 28 | @synthesize fields,encoding; 29 | 30 | 31 | - (id) initWithData:(NSData *)data formEncoding:(NSStringEncoding) formEncoding { 32 | if( nil == (self = [super init]) ) { 33 | return self; 34 | } 35 | 36 | fields = [[NSMutableDictionary alloc] initWithCapacity:1]; 37 | 38 | // In case encoding is not mentioned, 39 | encoding = contentTransferEncoding_unknown; 40 | 41 | char* bytes = (char*)data.bytes; 42 | NSUInteger length = data.length; 43 | int offset = 0; 44 | 45 | // split header into header fields, separated by \r\n 46 | uint16_t fields_separator = 0x0A0D; // \r\n 47 | while( offset < length - 2 ) { 48 | 49 | // the !isspace condition is to support header unfolding 50 | if( (*(uint16_t*) (bytes+offset) == fields_separator) && ((offset == length - 2) || !(isspace(bytes[offset+2])) )) { 51 | NSData* fieldData = [NSData dataWithBytesNoCopy:bytes length:offset freeWhenDone:NO]; 52 | MultipartMessageHeaderField* field = [[MultipartMessageHeaderField alloc] initWithData: fieldData contentEncoding:formEncoding]; 53 | if( field ) { 54 | [fields setObject:field forKey:field.name]; 55 | HTTPLogVerbose(@"MultipartFormDataParser: Processed Header field '%@'",field.name); 56 | } 57 | else { 58 | NSString* fieldStr = [[NSString alloc] initWithData:fieldData encoding:NSASCIIStringEncoding]; 59 | HTTPLogWarn(@"MultipartFormDataParser: Failed to parse MIME header field. Input ASCII string:%@",fieldStr); 60 | } 61 | 62 | // move to the next header field 63 | bytes += offset + 2; 64 | length -= offset + 2; 65 | offset = 0; 66 | continue; 67 | } 68 | ++ offset; 69 | } 70 | 71 | if( !fields.count ) { 72 | // it was an empty header. 73 | // we have to set default values. 74 | // default header. 75 | [fields setObject:@"text/plain" forKey:@"Content-Type"]; 76 | } 77 | 78 | return self; 79 | } 80 | 81 | - (NSString *)description { 82 | return [NSString stringWithFormat:@"%@",fields]; 83 | } 84 | 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | //----------------------------------------------------------------- 5 | // interface MultipartMessageHeaderField 6 | //----------------------------------------------------------------- 7 | 8 | @interface MultipartMessageHeaderField : NSObject { 9 | NSString* name; 10 | NSString* value; 11 | NSMutableDictionary* params; 12 | } 13 | 14 | @property (strong, readonly) NSString* value; 15 | @property (strong, readonly) NSDictionary* params; 16 | @property (strong, readonly) NSString* name; 17 | 18 | //- (id) initWithLine:(NSString*) line; 19 | //- (id) initWithName:(NSString*) paramName value:(NSString*) paramValue; 20 | 21 | - (id) initWithData:(NSData*) data contentEncoding:(NSStringEncoding) encoding; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.m: -------------------------------------------------------------------------------- 1 | 2 | #import "MultipartMessageHeaderField.h" 3 | #import "HTTPLogging.h" 4 | 5 | //----------------------------------------------------------------- 6 | #pragma mark log level 7 | 8 | #ifdef DEBUG 9 | static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; 10 | #else 11 | static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; 12 | #endif 13 | 14 | 15 | // helpers 16 | int findChar(const char* str,NSUInteger length, char c); 17 | NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding); 18 | 19 | //----------------------------------------------------------------- 20 | // interface MultipartMessageHeaderField (private) 21 | //----------------------------------------------------------------- 22 | 23 | 24 | @interface MultipartMessageHeaderField (private) 25 | -(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding; 26 | @end 27 | 28 | 29 | //----------------------------------------------------------------- 30 | // implementation MultipartMessageHeaderField 31 | //----------------------------------------------------------------- 32 | 33 | @implementation MultipartMessageHeaderField 34 | @synthesize name,value,params; 35 | 36 | - (id) initWithData:(NSData *)data contentEncoding:(NSStringEncoding)encoding { 37 | params = [[NSMutableDictionary alloc] initWithCapacity:1]; 38 | 39 | char* bytes = (char*)data.bytes; 40 | NSUInteger length = data.length; 41 | 42 | int separatorOffset = findChar(bytes, length, ':'); 43 | if( (-1 == separatorOffset) || (separatorOffset >= length-2) ) { 44 | HTTPLogError(@"MultipartFormDataParser: Bad format.No colon in field header."); 45 | // tear down 46 | return nil; 47 | } 48 | 49 | // header name is always ascii encoded; 50 | name = [[NSString alloc] initWithBytes: bytes length: separatorOffset encoding: NSASCIIStringEncoding]; 51 | if( nil == name ) { 52 | HTTPLogError(@"MultipartFormDataParser: Bad MIME header name."); 53 | // tear down 54 | return nil; 55 | } 56 | 57 | // skip the separator and the next ' ' symbol 58 | bytes += separatorOffset + 2; 59 | length -= separatorOffset + 2; 60 | 61 | separatorOffset = findChar(bytes, length, ';'); 62 | if( separatorOffset == -1 ) { 63 | // couldn't find ';', means we don't have extra params here. 64 | value = [[NSString alloc] initWithBytes:bytes length: length encoding:encoding]; 65 | 66 | if( nil == value ) { 67 | HTTPLogError(@"MultipartFormDataParser: Bad MIME header value for header name: '%@'",name); 68 | // tear down 69 | return nil; 70 | } 71 | return self; 72 | } 73 | 74 | value = [[NSString alloc] initWithBytes:bytes length: separatorOffset encoding:encoding]; 75 | HTTPLogVerbose(@"MultipartFormDataParser: Processing header field '%@' : '%@'",name,value); 76 | // skipe the separator and the next ' ' symbol 77 | bytes += separatorOffset + 2; 78 | length -= separatorOffset + 2; 79 | 80 | // parse the "params" part of the header 81 | if( ![self parseHeaderValueBytes:bytes length:length encoding:encoding] ) { 82 | NSString* paramsStr = [[NSString alloc] initWithBytes:bytes length:length encoding:NSASCIIStringEncoding]; 83 | HTTPLogError(@"MultipartFormDataParser: Bad params for header with name '%@' and value '%@'",name,value); 84 | HTTPLogError(@"MultipartFormDataParser: Params str: %@",paramsStr); 85 | 86 | return nil; 87 | } 88 | return self; 89 | } 90 | 91 | -(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding { 92 | int offset = 0; 93 | NSString* currentParam = nil; 94 | BOOL insideQuote = NO; 95 | while( offset < length ) { 96 | if( bytes[offset] == '\"' ) { 97 | if( !offset || bytes[offset-1] != '\\' ) { 98 | insideQuote = !insideQuote; 99 | } 100 | } 101 | 102 | // skip quoted symbols 103 | if( insideQuote ) { 104 | ++ offset; 105 | continue; 106 | } 107 | if( bytes[offset] == '=' ) { 108 | if( currentParam ) { 109 | // found '=' before terminating previous param. 110 | return NO; 111 | } 112 | currentParam = [[NSString alloc] initWithBytes:bytes length:offset encoding:NSASCIIStringEncoding]; 113 | 114 | bytes+=offset + 1; 115 | length -= offset + 1; 116 | offset = 0; 117 | continue; 118 | } 119 | if( bytes[offset] == ';' ) { 120 | if( !currentParam ) { 121 | // found ; before stating '='. 122 | HTTPLogError(@"MultipartFormDataParser: Unexpected ';' when parsing header"); 123 | return NO; 124 | } 125 | NSString* paramValue = extractParamValue(bytes, offset,encoding); 126 | if( nil == paramValue ) { 127 | HTTPLogWarn(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name); 128 | } 129 | else { 130 | #ifdef DEBUG 131 | if( [params objectForKey:currentParam] ) { 132 | HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in header %@",currentParam,name); 133 | } 134 | #endif 135 | [params setObject:paramValue forKey:currentParam]; 136 | HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue); 137 | } 138 | 139 | currentParam = nil; 140 | 141 | // ';' separator has ' ' following, skip them. 142 | bytes+=offset + 2; 143 | length -= offset + 2; 144 | offset = 0; 145 | } 146 | ++ offset; 147 | } 148 | 149 | // add last param 150 | if( insideQuote ) { 151 | HTTPLogWarn(@"MultipartFormDataParser: unterminated quote in header %@",name); 152 | // return YES; 153 | } 154 | if( currentParam ) { 155 | NSString* paramValue = extractParamValue(bytes, length, encoding); 156 | 157 | if( nil == paramValue ) { 158 | HTTPLogError(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name); 159 | } 160 | 161 | #ifdef DEBUG 162 | if( [params objectForKey:currentParam] ) { 163 | HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in one header",currentParam); 164 | } 165 | #endif 166 | [params setObject:paramValue forKey:currentParam]; 167 | HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue); 168 | currentParam = nil; 169 | } 170 | 171 | return YES; 172 | } 173 | 174 | - (NSString *)description { 175 | return [NSString stringWithFormat:@"%@:%@\n params: %@",name,value,params]; 176 | } 177 | 178 | @end 179 | 180 | int findChar(const char* str, NSUInteger length, char c) { 181 | int offset = 0; 182 | while( offset < length ) { 183 | if( str[offset] == c ) 184 | return offset; 185 | ++ offset; 186 | } 187 | return -1; 188 | } 189 | 190 | NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding) { 191 | if( !length ) 192 | return nil; 193 | NSMutableString* value = nil; 194 | 195 | if( bytes[0] == '"' ) { 196 | // values may be quoted. Strip the quotes to get what we need. 197 | value = [[NSMutableString alloc] initWithBytes:bytes + 1 length: length - 2 encoding:encoding]; 198 | } 199 | else { 200 | value = [[NSMutableString alloc] initWithBytes:bytes length: length encoding:encoding]; 201 | } 202 | // restore escaped symbols 203 | NSRange range= [value rangeOfString:@"\\"]; 204 | while ( range.length ) { 205 | [value deleteCharactersInRange:range]; 206 | range.location ++; 207 | range = [value rangeOfString:@"\\" options:NSLiteralSearch range: range]; 208 | } 209 | return value; 210 | } 211 | 212 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPResponse.h" 3 | 4 | @class HTTPConnection; 5 | 6 | /** 7 | * This is an asynchronous version of HTTPFileResponse. 8 | * It reads data from the given file asynchronously via GCD. 9 | * 10 | * It may be overriden to allow custom post-processing of the data that has been read from the file. 11 | * An example of this is the HTTPDynamicFileResponse class. 12 | **/ 13 | 14 | @interface HTTPAsyncFileResponse : NSObject 15 | { 16 | HTTPConnection *connection; 17 | 18 | NSString *filePath; 19 | UInt64 fileLength; 20 | UInt64 fileOffset; // File offset as pertains to data given to connection 21 | UInt64 readOffset; // File offset as pertains to data read from file (but maybe not returned to connection) 22 | 23 | BOOL aborted; 24 | 25 | NSData *data; 26 | 27 | int fileFD; 28 | void *readBuffer; 29 | NSUInteger readBufferSize; // Malloced size of readBuffer 30 | NSUInteger readBufferOffset; // Offset within readBuffer where the end of existing data is 31 | NSUInteger readRequestLength; 32 | dispatch_queue_t readQueue; 33 | dispatch_source_t readSource; 34 | BOOL readSourceSuspended; 35 | } 36 | 37 | - (id)initWithFilePath:(NSString *)filePath forConnection:(HTTPConnection *)connection; 38 | - (NSString *)filePath; 39 | 40 | @end 41 | 42 | /** 43 | * Explanation of Variables (excluding those that are obvious) 44 | * 45 | * fileOffset 46 | * This is the number of bytes that have been returned to the connection via the readDataOfLength method. 47 | * If 1KB of data has been read from the file, but none of that data has yet been returned to the connection, 48 | * then the fileOffset variable remains at zero. 49 | * This variable is used in the calculation of the isDone method. 50 | * Only after all data has been returned to the connection are we actually done. 51 | * 52 | * readOffset 53 | * Represents the offset of the file descriptor. 54 | * In other words, the file position indidcator for our read stream. 55 | * It might be easy to think of it as the total number of bytes that have been read from the file. 56 | * However, this isn't entirely accurate, as the setOffset: method may have caused us to 57 | * jump ahead in the file (lseek). 58 | * 59 | * readBuffer 60 | * Malloc'd buffer to hold data read from the file. 61 | * 62 | * readBufferSize 63 | * Total allocation size of malloc'd buffer. 64 | * 65 | * readBufferOffset 66 | * Represents the position in the readBuffer where we should store new bytes. 67 | * 68 | * readRequestLength 69 | * The total number of bytes that were requested from the connection. 70 | * It's OK if we return a lesser number of bytes to the connection. 71 | * It's NOT OK if we return a greater number of bytes to the connection. 72 | * Doing so would disrupt proper support for range requests. 73 | * If, however, the response is chunked then we don't need to worry about this. 74 | * Chunked responses inheritly don't support range requests. 75 | **/ 76 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPDataResponse.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPResponse.h" 3 | 4 | 5 | @interface HTTPDataResponse : NSObject 6 | { 7 | NSUInteger offset; 8 | NSData *data; 9 | } 10 | 11 | - (id)initWithData:(NSData *)data; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPDataResponse.m: -------------------------------------------------------------------------------- 1 | #import "HTTPDataResponse.h" 2 | #import "HTTPLogging.h" 3 | 4 | #if ! __has_feature(objc_arc) 5 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 6 | #endif 7 | 8 | // Log levels : off, error, warn, info, verbose 9 | // Other flags: trace 10 | static const int httpLogLevel = HTTP_LOG_LEVEL_OFF; // | HTTP_LOG_FLAG_TRACE; 11 | 12 | 13 | @implementation HTTPDataResponse 14 | 15 | - (id)initWithData:(NSData *)dataParam 16 | { 17 | if((self = [super init])) 18 | { 19 | HTTPLogTrace(); 20 | 21 | offset = 0; 22 | data = dataParam; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)dealloc 28 | { 29 | HTTPLogTrace(); 30 | 31 | } 32 | 33 | - (UInt64)contentLength 34 | { 35 | UInt64 result = (UInt64)[data length]; 36 | 37 | HTTPLogTrace2(@"%@[%p]: contentLength - %llu", THIS_FILE, self, result); 38 | 39 | return result; 40 | } 41 | 42 | - (UInt64)offset 43 | { 44 | HTTPLogTrace(); 45 | 46 | return offset; 47 | } 48 | 49 | - (void)setOffset:(UInt64)offsetParam 50 | { 51 | HTTPLogTrace2(@"%@[%p]: setOffset:%lu", THIS_FILE, self, (unsigned long)offset); 52 | 53 | offset = (NSUInteger)offsetParam; 54 | } 55 | 56 | - (NSData *)readDataOfLength:(NSUInteger)lengthParameter 57 | { 58 | HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)lengthParameter); 59 | 60 | NSUInteger remaining = [data length] - offset; 61 | NSUInteger length = lengthParameter < remaining ? lengthParameter : remaining; 62 | 63 | void *bytes = (void *)([data bytes] + offset); 64 | 65 | offset += length; 66 | 67 | return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:NO]; 68 | } 69 | 70 | - (BOOL)isDone 71 | { 72 | BOOL result = (offset == [data length]); 73 | 74 | HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO")); 75 | 76 | return result; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPResponse.h" 3 | #import "HTTPAsyncFileResponse.h" 4 | 5 | /** 6 | * This class is designed to assist with dynamic content. 7 | * Imagine you have a file that you want to make dynamic: 8 | * 9 | * 10 | * 11 | *

ComputerName Control Panel

12 | * ... 13 | *
  • System Time: SysTime
  • 14 | * 15 | * 16 | * 17 | * Now you could generate the entire file in Objective-C, 18 | * but this would be a horribly tedious process. 19 | * Beside, you want to design the file with professional tools to make it look pretty. 20 | * 21 | * So all you have to do is escape your dynamic content like this: 22 | * 23 | * ... 24 | *

    %%ComputerName%% Control Panel

    25 | * ... 26 | *
  • System Time: %%SysTime%%
  • 27 | * 28 | * And then you create an instance of this class with: 29 | * 30 | * - separator = @"%%" 31 | * - replacementDictionary = { "ComputerName"="Black MacBook", "SysTime"="2010-04-30 03:18:24" } 32 | * 33 | * This class will then perform the replacements for you, on the fly, as it reads the file data. 34 | * This class is also asynchronous, so it will perform the file IO using its own GCD queue. 35 | * 36 | * All keys for the replacementDictionary must be NSString's. 37 | * Values for the replacementDictionary may be NSString's, or any object that 38 | * returns what you want when its description method is invoked. 39 | **/ 40 | 41 | @interface HTTPDynamicFileResponse : HTTPAsyncFileResponse 42 | { 43 | NSData *separator; 44 | NSDictionary *replacementDict; 45 | } 46 | 47 | - (id)initWithFilePath:(NSString *)filePath 48 | forConnection:(HTTPConnection *)connection 49 | separator:(NSString *)separatorStr 50 | replacementDictionary:(NSDictionary *)dictionary; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.h: -------------------------------------------------------------------------------- 1 | #import "HTTPResponse.h" 2 | 3 | @interface HTTPErrorResponse : NSObject { 4 | NSInteger _status; 5 | } 6 | 7 | - (id)initWithErrorCode:(int)httpErrorCode; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.m: -------------------------------------------------------------------------------- 1 | #import "HTTPErrorResponse.h" 2 | 3 | @implementation HTTPErrorResponse 4 | 5 | -(id)initWithErrorCode:(int)httpErrorCode 6 | { 7 | if ((self = [super init])) 8 | { 9 | _status = httpErrorCode; 10 | } 11 | 12 | return self; 13 | } 14 | 15 | - (UInt64) contentLength { 16 | return 0; 17 | } 18 | 19 | - (UInt64) offset { 20 | return 0; 21 | } 22 | 23 | - (void)setOffset:(UInt64)offset { 24 | ; 25 | } 26 | 27 | - (NSData*) readDataOfLength:(NSUInteger)length { 28 | return nil; 29 | } 30 | 31 | - (BOOL) isDone { 32 | return YES; 33 | } 34 | 35 | - (NSInteger) status { 36 | return _status; 37 | } 38 | @end 39 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPFileResponse.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPResponse.h" 3 | 4 | @class HTTPConnection; 5 | 6 | 7 | @interface HTTPFileResponse : NSObject 8 | { 9 | HTTPConnection *connection; 10 | 11 | NSString *filePath; 12 | UInt64 fileLength; 13 | UInt64 fileOffset; 14 | 15 | BOOL aborted; 16 | 17 | int fileFD; 18 | void *buffer; 19 | NSUInteger bufferSize; 20 | } 21 | 22 | - (id)initWithFilePath:(NSString *)filePath forConnection:(HTTPConnection *)connection; 23 | - (NSString *)filePath; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPFileResponse.m: -------------------------------------------------------------------------------- 1 | #import "HTTPFileResponse.h" 2 | #import "HTTPConnection.h" 3 | #import "HTTPLogging.h" 4 | 5 | #import 6 | #import 7 | 8 | #if ! __has_feature(objc_arc) 9 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 10 | #endif 11 | 12 | // Log levels : off, error, warn, info, verbose 13 | // Other flags: trace 14 | static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE; 15 | 16 | #define NULL_FD -1 17 | 18 | 19 | @implementation HTTPFileResponse 20 | 21 | - (id)initWithFilePath:(NSString *)fpath forConnection:(HTTPConnection *)parent 22 | { 23 | if((self = [super init])) 24 | { 25 | HTTPLogTrace(); 26 | 27 | connection = parent; // Parents retain children, children do NOT retain parents 28 | 29 | fileFD = NULL_FD; 30 | filePath = [[fpath copy] stringByResolvingSymlinksInPath]; 31 | if (filePath == nil) 32 | { 33 | HTTPLogWarn(@"%@: Init failed - Nil filePath", THIS_FILE); 34 | 35 | return nil; 36 | } 37 | 38 | NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; 39 | if (fileAttributes == nil) 40 | { 41 | HTTPLogWarn(@"%@: Init failed - Unable to get file attributes. filePath: %@", THIS_FILE, filePath); 42 | 43 | return nil; 44 | } 45 | 46 | fileLength = (UInt64)[[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue]; 47 | fileOffset = 0; 48 | 49 | aborted = NO; 50 | 51 | // We don't bother opening the file here. 52 | // If this is a HEAD request we only need to know the fileLength. 53 | } 54 | return self; 55 | } 56 | 57 | - (void)abort 58 | { 59 | HTTPLogTrace(); 60 | 61 | [connection responseDidAbort:self]; 62 | aborted = YES; 63 | } 64 | 65 | - (BOOL)openFile 66 | { 67 | HTTPLogTrace(); 68 | 69 | fileFD = open([filePath UTF8String], O_RDONLY); 70 | if (fileFD == NULL_FD) 71 | { 72 | HTTPLogError(@"%@[%p]: Unable to open file. filePath: %@", THIS_FILE, self, filePath); 73 | 74 | [self abort]; 75 | return NO; 76 | } 77 | 78 | HTTPLogVerbose(@"%@[%p]: Open fd[%i] -> %@", THIS_FILE, self, fileFD, filePath); 79 | 80 | return YES; 81 | } 82 | 83 | - (BOOL)openFileIfNeeded 84 | { 85 | if (aborted) 86 | { 87 | // The file operation has been aborted. 88 | // This could be because we failed to open the file, 89 | // or the reading process failed. 90 | return NO; 91 | } 92 | 93 | if (fileFD != NULL_FD) 94 | { 95 | // File has already been opened. 96 | return YES; 97 | } 98 | 99 | return [self openFile]; 100 | } 101 | 102 | - (UInt64)contentLength 103 | { 104 | HTTPLogTrace(); 105 | 106 | return fileLength; 107 | } 108 | 109 | - (UInt64)offset 110 | { 111 | HTTPLogTrace(); 112 | 113 | return fileOffset; 114 | } 115 | 116 | - (void)setOffset:(UInt64)offset 117 | { 118 | HTTPLogTrace2(@"%@[%p]: setOffset:%llu", THIS_FILE, self, offset); 119 | 120 | if (![self openFileIfNeeded]) 121 | { 122 | // File opening failed, 123 | // or response has been aborted due to another error. 124 | return; 125 | } 126 | 127 | fileOffset = offset; 128 | 129 | off_t result = lseek(fileFD, (off_t)offset, SEEK_SET); 130 | if (result == -1) 131 | { 132 | HTTPLogError(@"%@[%p]: lseek failed - errno(%i) filePath(%@)", THIS_FILE, self, errno, filePath); 133 | 134 | [self abort]; 135 | } 136 | } 137 | 138 | - (NSData *)readDataOfLength:(NSUInteger)length 139 | { 140 | HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)length); 141 | 142 | if (![self openFileIfNeeded]) 143 | { 144 | // File opening failed, 145 | // or response has been aborted due to another error. 146 | return nil; 147 | } 148 | 149 | // Determine how much data we should read. 150 | // 151 | // It is OK if we ask to read more bytes than exist in the file. 152 | // It is NOT OK to over-allocate the buffer. 153 | 154 | UInt64 bytesLeftInFile = fileLength - fileOffset; 155 | 156 | NSUInteger bytesToRead = (NSUInteger)MIN(length, bytesLeftInFile); 157 | 158 | // Make sure buffer is big enough for read request. 159 | // Do not over-allocate. 160 | 161 | if (buffer == NULL || bufferSize < bytesToRead) 162 | { 163 | bufferSize = bytesToRead; 164 | buffer = reallocf(buffer, (size_t)bufferSize); 165 | 166 | if (buffer == NULL) 167 | { 168 | HTTPLogError(@"%@[%p]: Unable to allocate buffer", THIS_FILE, self); 169 | 170 | [self abort]; 171 | return nil; 172 | } 173 | } 174 | 175 | // Perform the read 176 | 177 | HTTPLogVerbose(@"%@[%p]: Attempting to read %lu bytes from file", THIS_FILE, self, (unsigned long)bytesToRead); 178 | 179 | ssize_t result = read(fileFD, buffer, bytesToRead); 180 | 181 | // Check the results 182 | 183 | if (result < 0) 184 | { 185 | HTTPLogError(@"%@: Error(%i) reading file(%@)", THIS_FILE, errno, filePath); 186 | 187 | [self abort]; 188 | return nil; 189 | } 190 | else if (result == 0) 191 | { 192 | HTTPLogError(@"%@: Read EOF on file(%@)", THIS_FILE, filePath); 193 | 194 | [self abort]; 195 | return nil; 196 | } 197 | else // (result > 0) 198 | { 199 | HTTPLogVerbose(@"%@[%p]: Read %ld bytes from file", THIS_FILE, self, (long)result); 200 | 201 | fileOffset += result; 202 | 203 | return [NSData dataWithBytes:buffer length:result]; 204 | } 205 | } 206 | 207 | - (BOOL)isDone 208 | { 209 | BOOL result = (fileOffset == fileLength); 210 | 211 | HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO")); 212 | 213 | return result; 214 | } 215 | 216 | - (NSString *)filePath 217 | { 218 | return filePath; 219 | } 220 | 221 | - (void)dealloc 222 | { 223 | HTTPLogTrace(); 224 | 225 | if (fileFD != NULL_FD) 226 | { 227 | HTTPLogVerbose(@"%@[%p]: Close fd[%i]", THIS_FILE, self, fileFD); 228 | 229 | close(fileFD); 230 | } 231 | 232 | if (buffer) 233 | free(buffer); 234 | 235 | } 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPResponse.h" 3 | 4 | 5 | @interface HTTPRedirectResponse : NSObject 6 | { 7 | NSString *redirectPath; 8 | } 9 | 10 | - (id)initWithPath:(NSString *)redirectPath; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.m: -------------------------------------------------------------------------------- 1 | #import "HTTPRedirectResponse.h" 2 | #import "HTTPLogging.h" 3 | 4 | #if ! __has_feature(objc_arc) 5 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 6 | #endif 7 | 8 | // Log levels : off, error, warn, info, verbose 9 | // Other flags: trace 10 | static const int httpLogLevel = HTTP_LOG_LEVEL_OFF; // | HTTP_LOG_FLAG_TRACE; 11 | 12 | 13 | @implementation HTTPRedirectResponse 14 | 15 | - (id)initWithPath:(NSString *)path 16 | { 17 | if ((self = [super init])) 18 | { 19 | HTTPLogTrace(); 20 | 21 | redirectPath = [path copy]; 22 | } 23 | return self; 24 | } 25 | 26 | - (UInt64)contentLength 27 | { 28 | return 0; 29 | } 30 | 31 | - (UInt64)offset 32 | { 33 | return 0; 34 | } 35 | 36 | - (void)setOffset:(UInt64)offset 37 | { 38 | // Nothing to do 39 | } 40 | 41 | - (NSData *)readDataOfLength:(NSUInteger)length 42 | { 43 | HTTPLogTrace(); 44 | 45 | return nil; 46 | } 47 | 48 | - (BOOL)isDone 49 | { 50 | return YES; 51 | } 52 | 53 | - (NSDictionary *)httpHeaders 54 | { 55 | HTTPLogTrace(); 56 | 57 | return [NSDictionary dictionaryWithObject:redirectPath forKey:@"Location"]; 58 | } 59 | 60 | - (NSInteger)status 61 | { 62 | HTTPLogTrace(); 63 | 64 | return 302; 65 | } 66 | 67 | - (void)dealloc 68 | { 69 | HTTPLogTrace(); 70 | 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Core/WebSocket.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class HTTPMessage; 4 | @class GCDAsyncSocket; 5 | 6 | 7 | #define WebSocketDidDieNotification @"WebSocketDidDie" 8 | 9 | @interface WebSocket : NSObject 10 | { 11 | dispatch_queue_t websocketQueue; 12 | 13 | HTTPMessage *request; 14 | GCDAsyncSocket *asyncSocket; 15 | 16 | NSData *term; 17 | 18 | BOOL isStarted; 19 | BOOL isOpen; 20 | BOOL isVersion76; 21 | 22 | id __unsafe_unretained delegate; 23 | } 24 | 25 | + (BOOL)isWebSocketRequest:(HTTPMessage *)request; 26 | 27 | - (id)initWithRequest:(HTTPMessage *)request socket:(GCDAsyncSocket *)socket; 28 | 29 | /** 30 | * Delegate option. 31 | * 32 | * In most cases it will be easier to subclass WebSocket, 33 | * but some circumstances may lead one to prefer standard delegate callbacks instead. 34 | **/ 35 | @property (/* atomic */ unsafe_unretained) id delegate; 36 | 37 | /** 38 | * The WebSocket class is thread-safe, generally via it's GCD queue. 39 | * All public API methods are thread-safe, 40 | * and the subclass API methods are thread-safe as they are all invoked on the same GCD queue. 41 | **/ 42 | @property (nonatomic, readonly) dispatch_queue_t websocketQueue; 43 | 44 | /** 45 | * Public API 46 | * 47 | * These methods are automatically called by the HTTPServer. 48 | * You may invoke the stop method yourself to close the WebSocket manually. 49 | **/ 50 | - (void)start; 51 | - (void)stop; 52 | 53 | /** 54 | * Public API 55 | * 56 | * Sends a message over the WebSocket. 57 | * This method is thread-safe. 58 | **/ 59 | - (void)sendMessage:(NSString *)msg; 60 | 61 | /** 62 | * Public API 63 | * 64 | * Sends a message over the WebSocket. 65 | * This method is thread-safe. 66 | **/ 67 | - (void)sendData:(NSData *)msg; 68 | 69 | /** 70 | * Subclass API 71 | * 72 | * These methods are designed to be overriden by subclasses. 73 | **/ 74 | - (void)didOpen; 75 | - (void)didReceiveMessage:(NSString *)msg; 76 | - (void)didClose; 77 | 78 | @end 79 | 80 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 81 | #pragma mark - 82 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 83 | 84 | /** 85 | * There are two ways to create your own custom WebSocket: 86 | * 87 | * - Subclass it and override the methods you're interested in. 88 | * - Use traditional delegate paradigm along with your own custom class. 89 | * 90 | * They both exist to allow for maximum flexibility. 91 | * In most cases it will be easier to subclass WebSocket. 92 | * However some circumstances may lead one to prefer standard delegate callbacks instead. 93 | * One such example, you're already subclassing another class, so subclassing WebSocket isn't an option. 94 | **/ 95 | 96 | @protocol WebSocketDelegate 97 | @optional 98 | 99 | - (void)webSocketDidOpen:(WebSocket *)ws; 100 | 101 | - (void)webSocket:(WebSocket *)ws didReceiveMessage:(NSString *)msg; 102 | 103 | - (void)webSocketDidClose:(WebSocket *)ws; 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2011, Deusty, LLC 4 | All rights reserved. 5 | 6 | Redistribution and use of this software in source and binary forms, 7 | with or without modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Neither the name of Deusty nor the names of its 14 | contributors may be used to endorse or promote products 15 | derived from this software without specific prior 16 | written permission of Deusty, LLC. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/README.markdown: -------------------------------------------------------------------------------- 1 | # CocoaHTTPServer 2 | 3 | [![Build Status](https://travis-ci.org/robbiehanson/CocoaHTTPServer.svg)](https://travis-ci.org/robbiehanson/CocoaHTTPServer) 4 | [![Version](http://img.shields.io/cocoapods/v/CocoaHTTPServer.svg?style=flat)](http://cocoapods.org/?q=CocoaHTTPServer) 5 | [![Platform](http://img.shields.io/cocoapods/p/CocoaHTTPServer.svg?style=flat)]() 6 | [![License](http://img.shields.io/cocoapods/l/CocoaHTTPServer.svg?style=flat)](https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE) 7 | 8 | CocoaHTTPServer is a small, lightweight, embeddable HTTP server for Mac OS X or iOS applications. 9 | 10 | Sometimes developers need an embedded HTTP server in their app. Perhaps it's a server application with remote monitoring. Or perhaps it's a desktop application using HTTP for the communication backend. Or perhaps it's an iOS app providing over-the-air access to documents. Whatever your reason, CocoaHTTPServer can get the job done. It provides: 11 | 12 | - Built in support for bonjour broadcasting 13 | - IPv4 and IPv6 support 14 | - Asynchronous networking using GCD and standard sockets 15 | - Password protection support 16 | - SSL/TLS encryption support 17 | - Extremely FAST and memory efficient 18 | - Extremely scalable (built entirely upon GCD) 19 | - Heavily commented code 20 | - Very easily extensible 21 | - WebDAV is supported too! 22 | 23 |
    24 | Can't find the answer to your question in any of the [wiki](https://github.com/robbiehanson/CocoaHTTPServer/wiki) articles? Try the **[mailing list](http://groups.google.com/group/cocoahttpserver)**. 25 |
    26 |
    27 | Love the project? Wanna buy me a coffee? (or a beer :D) [![donation](http://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BHF2DJRETGV5S) 28 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaAsyncSocket/About.txt: -------------------------------------------------------------------------------- 1 | The CocoaAsyncSocket project is under Public Domain license. 2 | http://code.google.com/p/cocoaasyncsocket/ 3 | 4 | The AsyncSocket project has been around since 2001 and is used in many applications and frameworks. -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/About.txt: -------------------------------------------------------------------------------- 1 | CocoaLumberjack is under the New BSD License. 2 | https://github.com/robbiehanson/CocoaLumberjack 3 | 4 | Extensive documentation, tutorials, etc are available: 5 | https://github.com/robbiehanson/CocoaLumberjack/wiki 6 | 7 | Overview of the project (copied from google code homepage): 8 | 9 | 10 | 11 | The lumberjack framework is fast & simple, yet powerful & flexible. 12 | It is similar in concept to other popular logging frameworks such as log4j, yet is designed specifically for objective-c, and takes advantage of features such as multi-threading, grand central dispatch (if available), lockless atomic operations, and the dynamic nature of the objective-c runtime. 13 | 14 | Lumberjack is fast: 15 | In most cases it is an order of magnitude faster than NSLog. 16 | 17 | Lumberjack is simple: 18 | It takes as little as a single line of code to configure lumberjack when your application launches. Then simply replace your NSLog statements with DDLog statements and that's about it. (And the DDLog macros have the exact same format and syntax as NSLog, so it's super easy.) 19 | 20 | Lumberjack is powerful: 21 | One log statement can be sent to multiple loggers, meaning you can log to a file and the console simultaneously. Want more? Create your own loggers (it's easy) and send your log statements over the network. Or to a database or distributed file system. The sky is the limit. 22 | 23 | Lumberjack is flexible: 24 | Configure your logging however you want. Change log levels per file (perfect for debugging). Change log levels per logger (verbose console, but concise log file). Change log levels per xcode configuration (verbose debug, but concise release). Have your log statements compiled out of the release build. Customize the number of log levels for your application. Add your own fine-grained logging. Dynamically change log levels during runtime. Choose how & when you want your log files to be rolled. Upload your log files to a central server. Compress archived log files to save disk space... 25 | 26 | 27 | 28 | This framework is for you if: 29 | 30 | You're looking for a way to track down that impossible-to-reproduce bug that keeps popping up in the field. 31 | You're frustrated with the super short console log on the iPhone. 32 | You're looking to take your application to the next level in terms of support and stability. 33 | You're looking for an enterprise level logging solution for your application (Mac or iPhone). -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/DDASLLogger.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "DDLog.h" 5 | 6 | /** 7 | * Welcome to Cocoa Lumberjack! 8 | * 9 | * The project page has a wealth of documentation if you have any questions. 10 | * https://github.com/robbiehanson/CocoaLumberjack 11 | * 12 | * If you're new to the project you may wish to read the "Getting Started" wiki. 13 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 14 | * 15 | * 16 | * This class provides a logger for the Apple System Log facility. 17 | * 18 | * As described in the "Getting Started" page, 19 | * the traditional NSLog() function directs it's output to two places: 20 | * 21 | * - Apple System Log 22 | * - StdErr (if stderr is a TTY) so log statements show up in Xcode console 23 | * 24 | * To duplicate NSLog() functionality you can simply add this logger and a tty logger. 25 | * However, if you instead choose to use file logging (for faster performance), 26 | * you may choose to use a file logger and a tty logger. 27 | **/ 28 | 29 | @interface DDASLLogger : DDAbstractLogger 30 | { 31 | aslclient client; 32 | } 33 | 34 | + (DDASLLogger *)sharedInstance; 35 | 36 | // Inherited from DDAbstractLogger 37 | 38 | // - (id )logFormatter; 39 | // - (void)setLogFormatter:(id )formatter; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/DDASLLogger.m: -------------------------------------------------------------------------------- 1 | #import "DDASLLogger.h" 2 | 3 | #import 4 | 5 | /** 6 | * Welcome to Cocoa Lumberjack! 7 | * 8 | * The project page has a wealth of documentation if you have any questions. 9 | * https://github.com/robbiehanson/CocoaLumberjack 10 | * 11 | * If you're new to the project you may wish to read the "Getting Started" wiki. 12 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 13 | **/ 14 | 15 | #if ! __has_feature(objc_arc) 16 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 17 | #endif 18 | 19 | 20 | @implementation DDASLLogger 21 | 22 | static DDASLLogger *sharedInstance; 23 | 24 | /** 25 | * The runtime sends initialize to each class in a program exactly one time just before the class, 26 | * or any class that inherits from it, is sent its first message from within the program. (Thus the 27 | * method may never be invoked if the class is not used.) The runtime sends the initialize message to 28 | * classes in a thread-safe manner. Superclasses receive this message before their subclasses. 29 | * 30 | * This method may also be called directly (assumably by accident), hence the safety mechanism. 31 | **/ 32 | + (void)initialize 33 | { 34 | static BOOL initialized = NO; 35 | if (!initialized) 36 | { 37 | initialized = YES; 38 | 39 | sharedInstance = [[DDASLLogger alloc] init]; 40 | } 41 | } 42 | 43 | + (DDASLLogger *)sharedInstance 44 | { 45 | return sharedInstance; 46 | } 47 | 48 | #pragma GCC diagnostic push 49 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 50 | - (id)init 51 | { 52 | if (sharedInstance != nil) 53 | { 54 | return nil; 55 | } 56 | 57 | if ((self = [super init])) 58 | { 59 | // A default asl client is provided for the main thread, 60 | // but background threads need to create their own client. 61 | 62 | client = asl_open(NULL, "com.apple.console", 0); 63 | } 64 | return self; 65 | } 66 | 67 | - (void)logMessage:(DDLogMessage *)logMessage 68 | { 69 | NSString *logMsg = logMessage->logMsg; 70 | 71 | if (formatter) 72 | { 73 | logMsg = [formatter formatLogMessage:logMessage]; 74 | } 75 | 76 | if (logMsg) 77 | { 78 | const char *msg = [logMsg UTF8String]; 79 | 80 | int aslLogLevel; 81 | switch (logMessage->logFlag) 82 | { 83 | // Note: By default ASL will filter anything above level 5 (Notice). 84 | // So our mappings shouldn't go above that level. 85 | 86 | case LOG_FLAG_ERROR : aslLogLevel = ASL_LEVEL_CRIT; break; 87 | case LOG_FLAG_WARN : aslLogLevel = ASL_LEVEL_ERR; break; 88 | case LOG_FLAG_INFO : aslLogLevel = ASL_LEVEL_WARNING; break; 89 | default : aslLogLevel = ASL_LEVEL_NOTICE; break; 90 | } 91 | 92 | asl_log(client, NULL, aslLogLevel, "%s", msg); 93 | } 94 | } 95 | #pragma GCC diagnostic pop 96 | 97 | - (NSString *)loggerName 98 | { 99 | return @"cocoa.lumberjack.aslLogger"; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/DDAbstractDatabaseLogger.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "DDLog.h" 4 | 5 | /** 6 | * Welcome to Cocoa Lumberjack! 7 | * 8 | * The project page has a wealth of documentation if you have any questions. 9 | * https://github.com/robbiehanson/CocoaLumberjack 10 | * 11 | * If you're new to the project you may wish to read the "Getting Started" wiki. 12 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 13 | * 14 | * 15 | * This class provides an abstract implementation of a database logger. 16 | * 17 | * That is, it provides the base implementation for a database logger to build atop of. 18 | * All that is needed for a concrete database logger is to extend this class 19 | * and override the methods in the implementation file that are prefixed with "db_". 20 | **/ 21 | 22 | @interface DDAbstractDatabaseLogger : DDAbstractLogger { 23 | @protected 24 | NSUInteger saveThreshold; 25 | NSTimeInterval saveInterval; 26 | NSTimeInterval maxAge; 27 | NSTimeInterval deleteInterval; 28 | BOOL deleteOnEverySave; 29 | 30 | BOOL saveTimerSuspended; 31 | NSUInteger unsavedCount; 32 | dispatch_time_t unsavedTime; 33 | dispatch_source_t saveTimer; 34 | dispatch_time_t lastDeleteTime; 35 | dispatch_source_t deleteTimer; 36 | } 37 | 38 | /** 39 | * Specifies how often to save the data to disk. 40 | * Since saving is an expensive operation (disk io) it is not done after every log statement. 41 | * These properties allow you to configure how/when the logger saves to disk. 42 | * 43 | * A save is done when either (whichever happens first): 44 | * 45 | * - The number of unsaved log entries reaches saveThreshold 46 | * - The amount of time since the oldest unsaved log entry was created reaches saveInterval 47 | * 48 | * You can optionally disable the saveThreshold by setting it to zero. 49 | * If you disable the saveThreshold you are entirely dependent on the saveInterval. 50 | * 51 | * You can optionally disable the saveInterval by setting it to zero (or a negative value). 52 | * If you disable the saveInterval you are entirely dependent on the saveThreshold. 53 | * 54 | * It's not wise to disable both saveThreshold and saveInterval. 55 | * 56 | * The default saveThreshold is 500. 57 | * The default saveInterval is 60 seconds. 58 | **/ 59 | @property (assign, readwrite) NSUInteger saveThreshold; 60 | @property (assign, readwrite) NSTimeInterval saveInterval; 61 | 62 | /** 63 | * It is likely you don't want the log entries to persist forever. 64 | * Doing so would allow the database to grow infinitely large over time. 65 | * 66 | * The maxAge property provides a way to specify how old a log statement can get 67 | * before it should get deleted from the database. 68 | * 69 | * The deleteInterval specifies how often to sweep for old log entries. 70 | * Since deleting is an expensive operation (disk io) is is done on a fixed interval. 71 | * 72 | * An alternative to the deleteInterval is the deleteOnEverySave option. 73 | * This specifies that old log entries should be deleted during every save operation. 74 | * 75 | * You can optionally disable the maxAge by setting it to zero (or a negative value). 76 | * If you disable the maxAge then old log statements are not deleted. 77 | * 78 | * You can optionally disable the deleteInterval by setting it to zero (or a negative value). 79 | * 80 | * If you disable both deleteInterval and deleteOnEverySave then old log statements are not deleted. 81 | * 82 | * It's not wise to enable both deleteInterval and deleteOnEverySave. 83 | * 84 | * The default maxAge is 7 days. 85 | * The default deleteInterval is 5 minutes. 86 | * The default deleteOnEverySave is NO. 87 | **/ 88 | @property (assign, readwrite) NSTimeInterval maxAge; 89 | @property (assign, readwrite) NSTimeInterval deleteInterval; 90 | @property (assign, readwrite) BOOL deleteOnEverySave; 91 | 92 | /** 93 | * Forces a save of any pending log entries (flushes log entries to disk). 94 | **/ 95 | - (void)savePendingLogEntries; 96 | 97 | /** 98 | * Removes any log entries that are older than maxAge. 99 | **/ 100 | - (void)deleteOldLogEntries; 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/DDTTYLogger.h: -------------------------------------------------------------------------------- 1 | #import 2 | #if TARGET_OS_IPHONE 3 | #import 4 | #else 5 | #import 6 | #endif 7 | 8 | #import "DDLog.h" 9 | 10 | /** 11 | * Welcome to Cocoa Lumberjack! 12 | * 13 | * The project page has a wealth of documentation if you have any questions. 14 | * https://github.com/robbiehanson/CocoaLumberjack 15 | * 16 | * If you're new to the project you may wish to read the "Getting Started" wiki. 17 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 18 | * 19 | * 20 | * This class provides a logger for Terminal output or Xcode console output, 21 | * depending on where you are running your code. 22 | * 23 | * As described in the "Getting Started" page, 24 | * the traditional NSLog() function directs it's output to two places: 25 | * 26 | * - Apple System Log (so it shows up in Console.app) 27 | * - StdErr (if stderr is a TTY, so log statements show up in Xcode console) 28 | * 29 | * To duplicate NSLog() functionality you can simply add this logger and an asl logger. 30 | * However, if you instead choose to use file logging (for faster performance), 31 | * you may choose to use only a file logger and a tty logger. 32 | **/ 33 | 34 | @interface DDTTYLogger : DDAbstractLogger 35 | { 36 | NSCalendar *calendar; 37 | NSUInteger calendarUnitFlags; 38 | 39 | NSString *appName; 40 | char *app; 41 | size_t appLen; 42 | 43 | NSString *processID; 44 | char *pid; 45 | size_t pidLen; 46 | 47 | BOOL colorsEnabled; 48 | NSMutableArray *colorProfilesArray; 49 | NSMutableDictionary *colorProfilesDict; 50 | } 51 | 52 | + (DDTTYLogger *)sharedInstance; 53 | 54 | /* Inherited from the DDLogger protocol: 55 | * 56 | * Formatters may optionally be added to any logger. 57 | * 58 | * If no formatter is set, the logger simply logs the message as it is given in logMessage, 59 | * or it may use its own built in formatting style. 60 | * 61 | * More information about formatters can be found here: 62 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters 63 | * 64 | * The actual implementation of these methods is inherited from DDAbstractLogger. 65 | 66 | - (id )logFormatter; 67 | - (void)setLogFormatter:(id )formatter; 68 | 69 | */ 70 | 71 | /** 72 | * Want to use different colors for different log levels? 73 | * Enable this property. 74 | * 75 | * If you run the application via the Terminal (not Xcode), 76 | * the logger will map colors to xterm-256color or xterm-color (if available). 77 | * 78 | * Xcode does NOT natively support colors in the Xcode debugging console. 79 | * You'll need to install the XcodeColors plugin to see colors in the Xcode console. 80 | * https://github.com/robbiehanson/XcodeColors 81 | * 82 | * The default value if NO. 83 | **/ 84 | @property (readwrite, assign) BOOL colorsEnabled; 85 | 86 | /** 87 | * The default color set (foregroundColor, backgroundColor) is: 88 | * 89 | * - LOG_FLAG_ERROR = (red, nil) 90 | * - LOG_FLAG_WARN = (orange, nil) 91 | * 92 | * You can customize the colors however you see fit. 93 | * Please note that you are passing a flag, NOT a level. 94 | * 95 | * GOOD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_FLAG_INFO]; // <- Good :) 96 | * BAD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_LEVEL_INFO]; // <- BAD! :( 97 | * 98 | * LOG_FLAG_INFO = 0...00100 99 | * LOG_LEVEL_INFO = 0...00111 <- Would match LOG_FLAG_INFO and LOG_FLAG_WARN and LOG_FLAG_ERROR 100 | * 101 | * If you run the application within Xcode, then the XcodeColors plugin is required. 102 | * 103 | * If you run the application from a shell, then DDTTYLogger will automatically map the given color to 104 | * the closest available color. (xterm-256color or xterm-color which have 256 and 16 supported colors respectively.) 105 | * 106 | * This method invokes setForegroundColor:backgroundColor:forFlag:context: and passes the default context (0). 107 | **/ 108 | #if TARGET_OS_IPHONE 109 | - (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask; 110 | #else 111 | - (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask; 112 | #endif 113 | 114 | /** 115 | * Just like setForegroundColor:backgroundColor:flag, but allows you to specify a particular logging context. 116 | * 117 | * A logging context is often used to identify log messages coming from a 3rd party framework, 118 | * although logging context's can be used for many different functions. 119 | * 120 | * Logging context's are explained in further detail here: 121 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomContext 122 | **/ 123 | #if TARGET_OS_IPHONE 124 | - (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask context:(int)ctxt; 125 | #else 126 | - (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask context:(int)ctxt; 127 | #endif 128 | 129 | /** 130 | * Similar to the methods above, but allows you to map DDLogMessage->tag to a particular color profile. 131 | * For example, you could do something like this: 132 | * 133 | * static NSString *const PurpleTag = @"PurpleTag"; 134 | * 135 | * #define DDLogPurple(frmt, ...) LOG_OBJC_TAG_MACRO(NO, 0, 0, 0, PurpleTag, frmt, ##__VA_ARGS__) 136 | * 137 | * And then in your applicationDidFinishLaunching, or wherever you configure Lumberjack: 138 | * 139 | * #if TARGET_OS_IPHONE 140 | * UIColor *purple = [UIColor colorWithRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0]; 141 | * #else 142 | * NSColor *purple = [NSColor colorWithCalibratedRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0]; 143 | * 144 | * [[DDTTYLogger sharedInstance] setForegroundColor:purple backgroundColor:nil forTag:PurpleTag]; 145 | * [DDLog addLogger:[DDTTYLogger sharedInstance]]; 146 | * 147 | * This would essentially give you a straight NSLog replacement that prints in purple: 148 | * 149 | * DDLogPurple(@"I'm a purple log message!"); 150 | **/ 151 | #if TARGET_OS_IPHONE 152 | - (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forTag:(id )tag; 153 | #else 154 | - (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forTag:(id )tag; 155 | #endif 156 | 157 | /** 158 | * Clearing color profiles. 159 | **/ 160 | - (void)clearColorsForFlag:(int)mask; 161 | - (void)clearColorsForFlag:(int)mask context:(int)context; 162 | - (void)clearColorsForTag:(id )tag; 163 | - (void)clearColorsForAllFlags; 164 | - (void)clearColorsForAllTags; 165 | - (void)clearAllColors; 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/ContextFilterLogFormatter.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DDLog.h" 3 | 4 | @class ContextFilterLogFormatter; 5 | 6 | /** 7 | * Welcome to Cocoa Lumberjack! 8 | * 9 | * The project page has a wealth of documentation if you have any questions. 10 | * https://github.com/robbiehanson/CocoaLumberjack 11 | * 12 | * If you're new to the project you may wish to read the "Getting Started" page. 13 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 14 | * 15 | * 16 | * This class provides a log formatter that filters log statements from a logging context not on the whitelist. 17 | * 18 | * A log formatter can be added to any logger to format and/or filter its output. 19 | * You can learn more about log formatters here: 20 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters 21 | * 22 | * You can learn more about logging context's here: 23 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomContext 24 | * 25 | * But here's a quick overview / refresher: 26 | * 27 | * Every log statement has a logging context. 28 | * These come from the underlying logging macros defined in DDLog.h. 29 | * The default logging context is zero. 30 | * You can define multiple logging context's for use in your application. 31 | * For example, logically separate parts of your app each have a different logging context. 32 | * Also 3rd party frameworks that make use of Lumberjack generally use their own dedicated logging context. 33 | **/ 34 | @interface ContextWhitelistFilterLogFormatter : NSObject 35 | 36 | - (id)init; 37 | 38 | - (void)addToWhitelist:(int)loggingContext; 39 | - (void)removeFromWhitelist:(int)loggingContext; 40 | 41 | - (NSArray *)whitelist; 42 | 43 | - (BOOL)isOnWhitelist:(int)loggingContext; 44 | 45 | @end 46 | 47 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 48 | #pragma mark - 49 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 50 | 51 | /** 52 | * This class provides a log formatter that filters log statements from a logging context on the blacklist. 53 | **/ 54 | @interface ContextBlacklistFilterLogFormatter : NSObject 55 | 56 | - (id)init; 57 | 58 | - (void)addToBlacklist:(int)loggingContext; 59 | - (void)removeFromBlacklist:(int)loggingContext; 60 | 61 | - (NSArray *)blacklist; 62 | 63 | - (BOOL)isOnBlacklist:(int)loggingContext; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/ContextFilterLogFormatter.m: -------------------------------------------------------------------------------- 1 | #import "ContextFilterLogFormatter.h" 2 | #import 3 | 4 | /** 5 | * Welcome to Cocoa Lumberjack! 6 | * 7 | * The project page has a wealth of documentation if you have any questions. 8 | * https://github.com/robbiehanson/CocoaLumberjack 9 | * 10 | * If you're new to the project you may wish to read the "Getting Started" wiki. 11 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 12 | **/ 13 | 14 | #if ! __has_feature(objc_arc) 15 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 16 | #endif 17 | 18 | @interface LoggingContextSet : NSObject 19 | 20 | - (void)addToSet:(int)loggingContext; 21 | - (void)removeFromSet:(int)loggingContext; 22 | 23 | - (NSArray *)currentSet; 24 | 25 | - (BOOL)isInSet:(int)loggingContext; 26 | 27 | @end 28 | 29 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 30 | #pragma mark - 31 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 32 | 33 | @implementation ContextWhitelistFilterLogFormatter 34 | { 35 | LoggingContextSet *contextSet; 36 | } 37 | 38 | - (id)init 39 | { 40 | if ((self = [super init])) 41 | { 42 | contextSet = [[LoggingContextSet alloc] init]; 43 | } 44 | return self; 45 | } 46 | 47 | 48 | - (void)addToWhitelist:(int)loggingContext 49 | { 50 | [contextSet addToSet:loggingContext]; 51 | } 52 | 53 | - (void)removeFromWhitelist:(int)loggingContext 54 | { 55 | [contextSet removeFromSet:loggingContext]; 56 | } 57 | 58 | - (NSArray *)whitelist 59 | { 60 | return [contextSet currentSet]; 61 | } 62 | 63 | - (BOOL)isOnWhitelist:(int)loggingContext 64 | { 65 | return [contextSet isInSet:loggingContext]; 66 | } 67 | 68 | - (NSString *)formatLogMessage:(DDLogMessage *)logMessage 69 | { 70 | if ([self isOnWhitelist:logMessage->logContext]) 71 | return logMessage->logMsg; 72 | else 73 | return nil; 74 | } 75 | 76 | @end 77 | 78 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 79 | #pragma mark - 80 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 81 | 82 | @implementation ContextBlacklistFilterLogFormatter 83 | { 84 | LoggingContextSet *contextSet; 85 | } 86 | 87 | - (id)init 88 | { 89 | if ((self = [super init])) 90 | { 91 | contextSet = [[LoggingContextSet alloc] init]; 92 | } 93 | return self; 94 | } 95 | 96 | 97 | - (void)addToBlacklist:(int)loggingContext 98 | { 99 | [contextSet addToSet:loggingContext]; 100 | } 101 | 102 | - (void)removeFromBlacklist:(int)loggingContext 103 | { 104 | [contextSet removeFromSet:loggingContext]; 105 | } 106 | 107 | - (NSArray *)blacklist 108 | { 109 | return [contextSet currentSet]; 110 | } 111 | 112 | - (BOOL)isOnBlacklist:(int)loggingContext 113 | { 114 | return [contextSet isInSet:loggingContext]; 115 | } 116 | 117 | - (NSString *)formatLogMessage:(DDLogMessage *)logMessage 118 | { 119 | if ([self isOnBlacklist:logMessage->logContext]) 120 | return nil; 121 | else 122 | return logMessage->logMsg; 123 | } 124 | 125 | @end 126 | 127 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 128 | #pragma mark - 129 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 130 | #pragma GCC diagnostic push 131 | #pragma GCC diagnostic ignored "-Wdeprecated" 132 | @implementation LoggingContextSet 133 | { 134 | OSSpinLock lock; 135 | NSMutableSet *set; 136 | } 137 | 138 | - (id)init 139 | { 140 | if ((self = [super init])) 141 | { 142 | set = [[NSMutableSet alloc] init]; 143 | } 144 | return self; 145 | } 146 | 147 | 148 | - (void)addToSet:(int)loggingContext 149 | { 150 | OSSpinLockLock(&lock); 151 | { 152 | [set addObject:@(loggingContext)]; 153 | } 154 | OSSpinLockUnlock(&lock); 155 | } 156 | 157 | - (void)removeFromSet:(int)loggingContext 158 | { 159 | OSSpinLockLock(&lock); 160 | { 161 | [set removeObject:@(loggingContext)]; 162 | } 163 | OSSpinLockUnlock(&lock); 164 | } 165 | 166 | - (NSArray *)currentSet 167 | { 168 | NSArray *result = nil; 169 | 170 | OSSpinLockLock(&lock); 171 | { 172 | result = [set allObjects]; 173 | } 174 | OSSpinLockUnlock(&lock); 175 | 176 | return result; 177 | } 178 | 179 | - (BOOL)isInSet:(int)loggingContext 180 | { 181 | BOOL result = NO; 182 | 183 | OSSpinLockLock(&lock); 184 | { 185 | result = [set containsObject:@(loggingContext)]; 186 | } 187 | OSSpinLockUnlock(&lock); 188 | 189 | return result; 190 | } 191 | #pragma GCC diagnostic pop 192 | @end 193 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/DispatchQueueLogFormatter.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "DDLog.h" 4 | 5 | 6 | /** 7 | * Welcome to Cocoa Lumberjack! 8 | * 9 | * The project page has a wealth of documentation if you have any questions. 10 | * https://github.com/robbiehanson/CocoaLumberjack 11 | * 12 | * If you're new to the project you may wish to read the "Getting Started" page. 13 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 14 | * 15 | * 16 | * This class provides a log formatter that prints the dispatch_queue label instead of the mach_thread_id. 17 | * 18 | * A log formatter can be added to any logger to format and/or filter its output. 19 | * You can learn more about log formatters here: 20 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters 21 | * 22 | * A typical NSLog (or DDTTYLogger) prints detailed info as [:]. 23 | * For example: 24 | * 25 | * 2011-10-17 20:21:45.435 AppName[19928:5207] Your log message here 26 | * 27 | * Where: 28 | * - 19928 = process id 29 | * - 5207 = thread id (mach_thread_id printed in hex) 30 | * 31 | * When using grand central dispatch (GCD), this information is less useful. 32 | * This is because a single serial dispatch queue may be run on any thread from an internally managed thread pool. 33 | * For example: 34 | * 35 | * 2011-10-17 20:32:31.111 AppName[19954:4d07] Message from my_serial_dispatch_queue 36 | * 2011-10-17 20:32:31.112 AppName[19954:5207] Message from my_serial_dispatch_queue 37 | * 2011-10-17 20:32:31.113 AppName[19954:2c55] Message from my_serial_dispatch_queue 38 | * 39 | * This formatter allows you to replace the standard [box:info] with the dispatch_queue name. 40 | * For example: 41 | * 42 | * 2011-10-17 20:32:31.111 AppName[img-scaling] Message from my_serial_dispatch_queue 43 | * 2011-10-17 20:32:31.112 AppName[img-scaling] Message from my_serial_dispatch_queue 44 | * 2011-10-17 20:32:31.113 AppName[img-scaling] Message from my_serial_dispatch_queue 45 | * 46 | * If the dispatch_queue doesn't have a set name, then it falls back to the thread name. 47 | * If the current thread doesn't have a set name, then it falls back to the mach_thread_id in hex (like normal). 48 | * 49 | * Note: If manually creating your own background threads (via NSThread/alloc/init or NSThread/detachNeThread), 50 | * you can use [[NSThread currentThread] setName:(NSString *)]. 51 | **/ 52 | @interface DispatchQueueLogFormatter : NSObject { 53 | @protected 54 | 55 | NSString *dateFormatString; 56 | } 57 | 58 | /** 59 | * Standard init method. 60 | * Configure using properties as desired. 61 | **/ 62 | - (id)init; 63 | 64 | /** 65 | * The minQueueLength restricts the minimum size of the [detail box]. 66 | * If the minQueueLength is set to 0, there is no restriction. 67 | * 68 | * For example, say a dispatch_queue has a label of "diskIO": 69 | * 70 | * If the minQueueLength is 0: [diskIO] 71 | * If the minQueueLength is 4: [diskIO] 72 | * If the minQueueLength is 5: [diskIO] 73 | * If the minQueueLength is 6: [diskIO] 74 | * If the minQueueLength is 7: [diskIO ] 75 | * If the minQueueLength is 8: [diskIO ] 76 | * 77 | * The default minQueueLength is 0 (no minimum, so [detail box] won't be padded). 78 | * 79 | * If you want every [detail box] to have the exact same width, 80 | * set both minQueueLength and maxQueueLength to the same value. 81 | **/ 82 | @property (assign) NSUInteger minQueueLength; 83 | 84 | /** 85 | * The maxQueueLength restricts the number of characters that will be inside the [detail box]. 86 | * If the maxQueueLength is 0, there is no restriction. 87 | * 88 | * For example, say a dispatch_queue has a label of "diskIO": 89 | * 90 | * If the maxQueueLength is 0: [diskIO] 91 | * If the maxQueueLength is 4: [disk] 92 | * If the maxQueueLength is 5: [diskI] 93 | * If the maxQueueLength is 6: [diskIO] 94 | * If the maxQueueLength is 7: [diskIO] 95 | * If the maxQueueLength is 8: [diskIO] 96 | * 97 | * The default maxQueueLength is 0 (no maximum, so [detail box] won't be truncated). 98 | * 99 | * If you want every [detail box] to have the exact same width, 100 | * set both minQueueLength and maxQueueLength to the same value. 101 | **/ 102 | @property (assign) NSUInteger maxQueueLength; 103 | 104 | /** 105 | * Sometimes queue labels have long names like "com.apple.main-queue", 106 | * but you'd prefer something shorter like simply "main". 107 | * 108 | * This method allows you to set such preferred replacements. 109 | * The above example is set by default. 110 | * 111 | * To remove/undo a previous replacement, invoke this method with nil for the 'shortLabel' parameter. 112 | **/ 113 | - (NSString *)replacementStringForQueueLabel:(NSString *)longLabel; 114 | - (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel; 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/DispatchQueueLogFormatter.m: -------------------------------------------------------------------------------- 1 | #import "DispatchQueueLogFormatter.h" 2 | #import 3 | 4 | /** 5 | * Welcome to Cocoa Lumberjack! 6 | * 7 | * The project page has a wealth of documentation if you have any questions. 8 | * https://github.com/robbiehanson/CocoaLumberjack 9 | * 10 | * If you're new to the project you may wish to read the "Getting Started" wiki. 11 | * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted 12 | **/ 13 | 14 | #if ! __has_feature(objc_arc) 15 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 16 | #endif 17 | 18 | #pragma GCC diagnostic push 19 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 20 | @implementation DispatchQueueLogFormatter 21 | { 22 | int32_t atomicLoggerCount; 23 | NSDateFormatter *threadUnsafeDateFormatter; // Use [self stringFromDate] 24 | 25 | OSSpinLock lock; 26 | 27 | NSUInteger _minQueueLength; // _prefix == Only access via atomic property 28 | NSUInteger _maxQueueLength; // _prefix == Only access via atomic property 29 | NSMutableDictionary *_replacements; // _prefix == Only access from within spinlock 30 | } 31 | 32 | - (id)init 33 | { 34 | if ((self = [super init])) 35 | { 36 | dateFormatString = @"yyyy-MM-dd HH:mm:ss:SSS"; 37 | 38 | atomicLoggerCount = 0; 39 | threadUnsafeDateFormatter = nil; 40 | 41 | _minQueueLength = 0; 42 | _maxQueueLength = 0; 43 | _replacements = [[NSMutableDictionary alloc] init]; 44 | 45 | // Set default replacements: 46 | 47 | [_replacements setObject:@"main" forKey:@"com.apple.main-thread"]; 48 | } 49 | return self; 50 | } 51 | 52 | 53 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 54 | #pragma mark Configuration 55 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 56 | 57 | @synthesize minQueueLength = _minQueueLength; 58 | @synthesize maxQueueLength = _maxQueueLength; 59 | 60 | - (NSString *)replacementStringForQueueLabel:(NSString *)longLabel 61 | { 62 | NSString *result = nil; 63 | 64 | OSSpinLockLock(&lock); 65 | { 66 | result = [_replacements objectForKey:longLabel]; 67 | } 68 | OSSpinLockUnlock(&lock); 69 | 70 | return result; 71 | } 72 | 73 | - (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel 74 | { 75 | OSSpinLockLock(&lock); 76 | { 77 | if (shortLabel) 78 | [_replacements setObject:shortLabel forKey:longLabel]; 79 | else 80 | [_replacements removeObjectForKey:longLabel]; 81 | } 82 | OSSpinLockUnlock(&lock); 83 | } 84 | 85 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 86 | #pragma mark DDLogFormatter 87 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 88 | 89 | - (NSString *)stringFromDate:(NSDate *)date 90 | { 91 | int32_t loggerCount = OSAtomicAdd32(0, &atomicLoggerCount); 92 | 93 | if (loggerCount <= 1) 94 | { 95 | // Single-threaded mode. 96 | 97 | if (threadUnsafeDateFormatter == nil) 98 | { 99 | threadUnsafeDateFormatter = [[NSDateFormatter alloc] init]; 100 | [threadUnsafeDateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; 101 | [threadUnsafeDateFormatter setDateFormat:dateFormatString]; 102 | } 103 | 104 | return [threadUnsafeDateFormatter stringFromDate:date]; 105 | } 106 | else 107 | { 108 | // Multi-threaded mode. 109 | // NSDateFormatter is NOT thread-safe. 110 | 111 | NSString *key = @"DispatchQueueLogFormatter_NSDateFormatter"; 112 | 113 | NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary]; 114 | NSDateFormatter *dateFormatter = [threadDictionary objectForKey:key]; 115 | 116 | if (dateFormatter == nil) 117 | { 118 | dateFormatter = [[NSDateFormatter alloc] init]; 119 | [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; 120 | [dateFormatter setDateFormat:dateFormatString]; 121 | 122 | [threadDictionary setObject:dateFormatter forKey:key]; 123 | } 124 | 125 | return [dateFormatter stringFromDate:date]; 126 | } 127 | } 128 | 129 | - (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage 130 | { 131 | // As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue 132 | 133 | NSUInteger minQueueLength = self.minQueueLength; 134 | NSUInteger maxQueueLength = self.maxQueueLength; 135 | 136 | // Get the name of the queue, thread, or machID (whichever we are to use). 137 | 138 | NSString *queueThreadLabel = nil; 139 | 140 | BOOL useQueueLabel = YES; 141 | BOOL useThreadName = NO; 142 | 143 | if (logMessage->queueLabel) 144 | { 145 | // If you manually create a thread, it's dispatch_queue will have one of the thread names below. 146 | // Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID. 147 | 148 | char *names[] = { "com.apple.root.low-priority", 149 | "com.apple.root.default-priority", 150 | "com.apple.root.high-priority", 151 | "com.apple.root.low-overcommit-priority", 152 | "com.apple.root.default-overcommit-priority", 153 | "com.apple.root.high-overcommit-priority" }; 154 | 155 | int length = sizeof(names) / sizeof(char *); 156 | 157 | int i; 158 | for (i = 0; i < length; i++) 159 | { 160 | if (strcmp(logMessage->queueLabel, names[i]) == 0) 161 | { 162 | useQueueLabel = NO; 163 | useThreadName = [logMessage->threadName length] > 0; 164 | break; 165 | } 166 | } 167 | } 168 | else 169 | { 170 | useQueueLabel = NO; 171 | useThreadName = [logMessage->threadName length] > 0; 172 | } 173 | 174 | if (useQueueLabel || useThreadName) 175 | { 176 | NSString *fullLabel; 177 | NSString *abrvLabel; 178 | 179 | if (useQueueLabel) 180 | fullLabel = @(logMessage->queueLabel); 181 | else 182 | fullLabel = logMessage->threadName; 183 | 184 | OSSpinLockLock(&lock); 185 | { 186 | abrvLabel = [_replacements objectForKey:fullLabel]; 187 | } 188 | OSSpinLockUnlock(&lock); 189 | 190 | if (abrvLabel) 191 | queueThreadLabel = abrvLabel; 192 | else 193 | queueThreadLabel = fullLabel; 194 | } 195 | else 196 | { 197 | queueThreadLabel = [NSString stringWithFormat:@"%x", logMessage->machThreadID]; 198 | } 199 | 200 | // Now use the thread label in the output 201 | 202 | NSUInteger labelLength = [queueThreadLabel length]; 203 | 204 | // labelLength > maxQueueLength : truncate 205 | // labelLength < minQueueLength : padding 206 | // : exact 207 | 208 | if ((maxQueueLength > 0) && (labelLength > maxQueueLength)) 209 | { 210 | // Truncate 211 | 212 | return [queueThreadLabel substringToIndex:maxQueueLength]; 213 | } 214 | else if (labelLength < minQueueLength) 215 | { 216 | // Padding 217 | 218 | NSUInteger numSpaces = minQueueLength - labelLength; 219 | 220 | char spaces[numSpaces + 1]; 221 | memset(spaces, ' ', numSpaces); 222 | spaces[numSpaces] = '\0'; 223 | 224 | return [NSString stringWithFormat:@"%@%s", queueThreadLabel, spaces]; 225 | } 226 | else 227 | { 228 | // Exact 229 | 230 | return queueThreadLabel; 231 | } 232 | } 233 | 234 | - (NSString *)formatLogMessage:(DDLogMessage *)logMessage 235 | { 236 | NSString *timestamp = [self stringFromDate:(logMessage->timestamp)]; 237 | NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage]; 238 | 239 | return [NSString stringWithFormat:@"%@ [%@] %@", timestamp, queueThreadLabel, logMessage->logMsg]; 240 | } 241 | 242 | - (void)didAddToLogger:(id )logger 243 | { 244 | OSAtomicIncrement32(&atomicLoggerCount); 245 | } 246 | 247 | - (void)willRemoveFromLogger:(id )logger 248 | { 249 | OSAtomicDecrement32(&atomicLoggerCount); 250 | } 251 | #pragma GCC diagnostic pop 252 | 253 | @end 254 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/README.txt: -------------------------------------------------------------------------------- 1 | This folder contains some sample formatters that may be helpful. 2 | 3 | Feel free to change them, extend them, or use them as the basis for your own custom formatter(s). 4 | 5 | More information about creating your own custom formatters can be found on the wiki: 6 | https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters 7 | 8 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/HTTPResponseProxy.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPResponse.h" 3 | 4 | // Wraps an HTTPResponse object to allow setting a custom status code 5 | // without needing to create subclasses of every response. 6 | @interface HTTPResponseProxy : NSObject 7 | 8 | @property (nonatomic) NSObject *response; 9 | @property (nonatomic) NSInteger status; 10 | 11 | - (NSInteger)customStatus; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/HTTPResponseProxy.m: -------------------------------------------------------------------------------- 1 | #import "HTTPResponseProxy.h" 2 | 3 | @implementation HTTPResponseProxy 4 | 5 | @synthesize response; 6 | @synthesize status; 7 | 8 | - (NSInteger)status { 9 | if (status != 0) { 10 | return status; 11 | } else if ([response respondsToSelector:@selector(status)]) { 12 | return [response status]; 13 | } 14 | 15 | return 200; 16 | } 17 | 18 | - (void)setStatus:(NSInteger)statusCode { 19 | status = statusCode; 20 | } 21 | 22 | - (NSInteger)customStatus { 23 | return status; 24 | } 25 | 26 | // Implement the required HTTPResponse methods 27 | - (UInt64)contentLength { 28 | if (response) { 29 | return [response contentLength]; 30 | } else { 31 | return 0; 32 | } 33 | } 34 | 35 | - (UInt64)offset { 36 | if (response) { 37 | return [response offset]; 38 | } else { 39 | return 0; 40 | } 41 | } 42 | 43 | - (void)setOffset:(UInt64)offset { 44 | if (response) { 45 | [response setOffset:offset]; 46 | } 47 | } 48 | 49 | - (NSData *)readDataOfLength:(NSUInteger)length { 50 | if (response) { 51 | return [response readDataOfLength:length]; 52 | } else { 53 | return nil; 54 | } 55 | } 56 | 57 | - (BOOL)isDone { 58 | if (response) { 59 | return [response isDone]; 60 | } else { 61 | return YES; 62 | } 63 | } 64 | 65 | // Forward all other invocations to the actual response object 66 | - (void)forwardInvocation:(NSInvocation *)invocation { 67 | if ([response respondsToSelector:[invocation selector]]) { 68 | [invocation invokeWithTarget:response]; 69 | } else { 70 | [super forwardInvocation:invocation]; 71 | } 72 | } 73 | 74 | - (BOOL)respondsToSelector:(SEL)selector { 75 | if ([super respondsToSelector:selector]) 76 | return YES; 77 | 78 | return [response respondsToSelector:selector]; 79 | } 80 | 81 | @end 82 | 83 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Matt Stevens 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/README.md: -------------------------------------------------------------------------------- 1 | # RoutingHTTPServer 2 | 3 | Adds a Sinatra-inspired routing API on top of [CocoaHTTPServer](https://github.com/robbiehanson/CocoaHTTPServer). 4 | 5 | Supports iOS 4+ and OS X 10.7+ 6 | 7 | ## Installation 8 | 9 | 1. Add [CocoaHTTPServer](https://github.com/robbiehanson/CocoaHTTPServer) to your project 10 | 2. Add the files in the Source directory 11 | 12 | ## Usage 13 | 14 | http = [[RoutingHTTPServer alloc] init]; 15 | [http setPort:8000]; 16 | [http setDefaultHeader:@"Server" value:@"YourAwesomeApp/1.0"]; 17 | 18 | [http handleMethod:@"GET" withPath:@"/hello" block:^(RouteRequest *request, RouteResponse *response) { 19 | [response setHeader:@"Content-Type" value:@"text/plain"]; 20 | [response respondWithString:@"Hello!"]; 21 | }]; 22 | 23 | Convenience methods are available for GET/POST/PUT/DELETE: 24 | 25 | [http get:@"/hello/:name" withBlock:^(RouteRequest *request, RouteResponse *response) { 26 | [response setHeader:@"Content-Type" value:@"text/plain"]; 27 | [response respondWithString:[NSString stringWithFormat:@"Hello %@!", [request param:@"name"]]]; 28 | }]; 29 | 30 | Note that in this example the path is `/hello/:name`, this will match `/hello/world`, `/hello/you`, and so forth. The named parameters in the path are added to the params dictionary in the request object. Query parameters are also included in this dictionary. 31 | 32 | Paths can use wildcards: 33 | 34 | [http get:@"/files/*.*" withBlock:^(RouteRequest *request, RouteResponse *response) { 35 | // The "wildcards" parameter is an NSArray of wildcard matches 36 | }]; 37 | 38 | Or your own regular expressions by wrapping the string in braces: 39 | 40 | [http get:@"{^/page/(\\d+)}" withBlock:^(RouteRequest *request, RouteResponse *response) { 41 | // The "captures" parameter is an NSArray of capture groups 42 | }]; 43 | 44 | Routes can also be handled with selectors: 45 | 46 | - (void)setupRoutes { 47 | [http handleMethod:@"GET" withPath:@"/hello" target:self selector:@selector(handleHelloRequest:withResponse:)]; 48 | } 49 | 50 | - (void)handleHelloRequest:(RouteRequest *)request withResponse:(RouteResponse *)response { 51 | [response respondWithString:@"Hello!"]; 52 | } 53 | 54 | RouteResponses can respond with an NSString or NSData object, a path to a file, or an existing HTTPResponse class. Responses can also be empty as long as a status code or custom header is set. For example, to perform a redirect: 55 | 56 | [http get:@"/old" withBlock:^(RouteRequest *request, RouteResponse *response) { 57 | [response setStatusCode:302]; // or 301 58 | [response setHeader:@"Location" value:[self.baseURL stringByAppendingString:@"/new"]]; 59 | }]; 60 | 61 | The server object was also given a couple of enhancements: 62 | 63 | * Default headers can be set through `setDefaultHeader:value:` or a dictionary passed to `setDefaultHeaders`. This allows you to add things like a Server header. 64 | 65 | * The Connection header is added to every response. You can set it explicitly in your response object if you want to force closing of a keep-alive connection. 66 | 67 | * The dispatch queue on which routes are processed can be changed. By default routes are processed on CocoaHTTPServer's connection queue, changing this to `dispatch_get_main_queue()` will process all routes on the main thread instead. Connection handling still occurs in the background, only the route handlers are impacted. 68 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/Route.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "RoutingHTTPServer.h" 3 | 4 | @interface Route : NSObject 5 | 6 | @property (nonatomic) NSRegularExpression *regex; 7 | @property (nonatomic, copy) RequestHandler handler; 8 | 9 | #if __has_feature(objc_arc_weak) 10 | @property (nonatomic, weak) id target; 11 | #else 12 | @property (nonatomic, assign) id target; 13 | #endif 14 | 15 | @property (nonatomic, assign) SEL selector; 16 | @property (nonatomic) NSArray *keys; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/Route.m: -------------------------------------------------------------------------------- 1 | #import "Route.h" 2 | 3 | @implementation Route 4 | 5 | @synthesize regex; 6 | @synthesize handler; 7 | @synthesize target; 8 | @synthesize selector; 9 | @synthesize keys; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/RouteRequest.h: -------------------------------------------------------------------------------- 1 | #import 2 | @class HTTPMessage; 3 | 4 | @interface RouteRequest : NSObject 5 | 6 | @property (nonatomic, readonly) NSDictionary *headers; 7 | @property (nonatomic, readonly) NSDictionary *params; 8 | 9 | - (id)initWithHTTPMessage:(HTTPMessage *)msg parameters:(NSDictionary *)params; 10 | - (NSString *)header:(NSString *)field; 11 | - (id)param:(NSString *)name; 12 | - (NSString *)method; 13 | - (NSURL *)url; 14 | - (NSData *)body; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/RouteRequest.m: -------------------------------------------------------------------------------- 1 | #import "RouteRequest.h" 2 | #import "HTTPMessage.h" 3 | 4 | @implementation RouteRequest { 5 | HTTPMessage *message; 6 | } 7 | 8 | @synthesize params; 9 | 10 | - (id)initWithHTTPMessage:(HTTPMessage *)msg parameters:(NSDictionary *)parameters { 11 | if (self = [super init]) { 12 | params = parameters; 13 | message = msg; 14 | } 15 | return self; 16 | } 17 | 18 | - (NSDictionary *)headers { 19 | return [message allHeaderFields]; 20 | } 21 | 22 | - (NSString *)header:(NSString *)field { 23 | return [message headerField:field]; 24 | } 25 | 26 | - (id)param:(NSString *)name { 27 | return [params objectForKey:name]; 28 | } 29 | 30 | - (NSString *)method { 31 | return [message method]; 32 | } 33 | 34 | - (NSURL *)url { 35 | return [message url]; 36 | } 37 | 38 | - (NSData *)body { 39 | return [message body]; 40 | } 41 | 42 | - (NSString *)description { 43 | NSData *data = [message messageData]; 44 | return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/RouteResponse.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPResponse.h" 3 | @class HTTPConnection; 4 | @class HTTPResponseProxy; 5 | 6 | @interface RouteResponse : NSObject 7 | 8 | @property (nonatomic, assign, readonly) HTTPConnection *connection; 9 | @property (nonatomic, readonly) NSDictionary *headers; 10 | @property (nonatomic, strong) NSObject *response; 11 | @property (nonatomic, readonly) NSObject *proxiedResponse; 12 | @property (nonatomic) NSInteger statusCode; 13 | 14 | - (id)initWithConnection:(HTTPConnection *)theConnection; 15 | - (void)setHeader:(NSString *)field value:(NSString *)value; 16 | - (void)respondWithString:(NSString *)string; 17 | - (void)respondWithString:(NSString *)string encoding:(NSStringEncoding)encoding; 18 | - (void)respondWithData:(NSData *)data; 19 | - (void)respondWithFile:(NSString *)path; 20 | - (void)respondWithFile:(NSString *)path async:(BOOL)async; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/RouteResponse.m: -------------------------------------------------------------------------------- 1 | #import "RouteResponse.h" 2 | #import "HTTPConnection.h" 3 | #import "HTTPDataResponse.h" 4 | #import "HTTPFileResponse.h" 5 | #import "HTTPAsyncFileResponse.h" 6 | #import "HTTPResponseProxy.h" 7 | 8 | @implementation RouteResponse { 9 | NSMutableDictionary *headers; 10 | HTTPResponseProxy *proxy; 11 | } 12 | 13 | @synthesize connection; 14 | @synthesize headers; 15 | 16 | - (id)initWithConnection:(HTTPConnection *)theConnection { 17 | if (self = [super init]) { 18 | connection = theConnection; 19 | headers = [[NSMutableDictionary alloc] init]; 20 | proxy = [[HTTPResponseProxy alloc] init]; 21 | } 22 | return self; 23 | } 24 | 25 | - (NSObject *)response { 26 | return proxy.response; 27 | } 28 | 29 | - (void)setResponse:(NSObject *)response { 30 | proxy.response = response; 31 | } 32 | 33 | - (NSObject *)proxiedResponse { 34 | if (proxy.response != nil || proxy.customStatus != 0 || [headers count] > 0) { 35 | return proxy; 36 | } 37 | 38 | return nil; 39 | } 40 | 41 | - (NSInteger)statusCode { 42 | return proxy.status; 43 | } 44 | 45 | - (void)setStatusCode:(NSInteger)status { 46 | proxy.status = status; 47 | } 48 | 49 | - (void)setHeader:(NSString *)field value:(NSString *)value { 50 | [headers setObject:value forKey:field]; 51 | } 52 | 53 | - (void)respondWithString:(NSString *)string { 54 | [self respondWithString:string encoding:NSUTF8StringEncoding]; 55 | } 56 | 57 | - (void)respondWithString:(NSString *)string encoding:(NSStringEncoding)encoding { 58 | [self respondWithData:[string dataUsingEncoding:encoding]]; 59 | } 60 | 61 | - (void)respondWithData:(NSData *)data { 62 | self.response = [[HTTPDataResponse alloc] initWithData:data]; 63 | } 64 | 65 | - (void)respondWithFile:(NSString *)path { 66 | [self respondWithFile:path async:NO]; 67 | } 68 | 69 | - (void)respondWithFile:(NSString *)path async:(BOOL)async { 70 | if (async) { 71 | self.response = [[HTTPAsyncFileResponse alloc] initWithFilePath:path forConnection:connection]; 72 | } else { 73 | self.response = [[HTTPFileResponse alloc] initWithFilePath:path forConnection:connection]; 74 | } 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/RoutingConnection.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPConnection.h" 3 | @class RoutingHTTPServer; 4 | 5 | @interface RoutingConnection : HTTPConnection 6 | @end 7 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/RoutingConnection.m: -------------------------------------------------------------------------------- 1 | #import "RoutingConnection.h" 2 | #import "RoutingHTTPServer.h" 3 | #import "HTTPMessage.h" 4 | #import "HTTPResponseProxy.h" 5 | 6 | @implementation RoutingConnection { 7 | __unsafe_unretained RoutingHTTPServer *http; 8 | NSDictionary *headers; 9 | } 10 | 11 | - (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig *)aConfig { 12 | if (self = [super initWithAsyncSocket:newSocket configuration:aConfig]) { 13 | NSAssert([config.server isKindOfClass:[RoutingHTTPServer class]], 14 | @"A RoutingConnection is being used with a server that is not a RoutingHTTPServer"); 15 | 16 | http = (RoutingHTTPServer *)config.server; 17 | } 18 | return self; 19 | } 20 | 21 | - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path { 22 | 23 | if ([http supportsMethod:method]) 24 | return YES; 25 | 26 | return [super supportsMethod:method atPath:path]; 27 | } 28 | 29 | - (BOOL)shouldHandleRequestForMethod:(NSString *)method atPath:(NSString *)path { 30 | // The default implementation is strict about the use of Content-Length. Either 31 | // a given method + path combination must *always* include data or *never* 32 | // include data. The routing connection is lenient, a POST that sometimes does 33 | // not include data or a GET that sometimes does is fine. It is up to the route 34 | // implementations to decide how to handle these situations. 35 | return YES; 36 | } 37 | 38 | - (void)processBodyData:(NSData *)postDataChunk { 39 | BOOL result = [request appendData:postDataChunk]; 40 | if (!result) { 41 | // TODO: Log 42 | } 43 | } 44 | #pragma GCC diagnostic push 45 | #pragma GCC diagnostic ignored "-Wundeclared-selector" 46 | - (NSObject *)httpResponseForMethod:(NSString *)method URI:(NSString *)path { 47 | NSURL *url = [request url]; 48 | NSString *query = nil; 49 | NSDictionary *params = [NSDictionary dictionary]; 50 | headers = nil; 51 | 52 | if (url) { 53 | path = [url path]; // Strip the query string from the path 54 | query = [url query]; 55 | if (query) { 56 | params = [self parseParams:query]; 57 | } 58 | } 59 | 60 | RouteResponse *response = [http routeMethod:method withPath:path parameters:params request:request connection:self]; 61 | if (response != nil) { 62 | headers = response.headers; 63 | return response.proxiedResponse; 64 | } 65 | 66 | // Set a MIME type for static files if possible 67 | NSObject *staticResponse = [super httpResponseForMethod:method URI:path]; 68 | if (staticResponse && [staticResponse respondsToSelector:@selector(filePath)]) { 69 | NSString *mimeType = [http mimeTypeForPath:[staticResponse performSelector:@selector(filePath)]]; 70 | if (mimeType) { 71 | headers = [NSDictionary dictionaryWithObject:mimeType forKey:@"Content-Type"]; 72 | } 73 | } 74 | return staticResponse; 75 | } 76 | #pragma GCC diagnostic pop 77 | - (void)responseHasAvailableData:(NSObject *)sender { 78 | HTTPResponseProxy *proxy = (HTTPResponseProxy *)httpResponse; 79 | if (proxy.response == sender) { 80 | [super responseHasAvailableData:httpResponse]; 81 | } 82 | } 83 | 84 | - (void)responseDidAbort:(NSObject *)sender { 85 | HTTPResponseProxy *proxy = (HTTPResponseProxy *)httpResponse; 86 | if (proxy.response == sender) { 87 | [super responseDidAbort:httpResponse]; 88 | } 89 | } 90 | 91 | - (void)setHeadersForResponse:(HTTPMessage *)response isError:(BOOL)isError { 92 | [http.defaultHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) { 93 | [response setHeaderField:field value:value]; 94 | }]; 95 | 96 | if (headers && !isError) { 97 | [headers enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) { 98 | [response setHeaderField:field value:value]; 99 | }]; 100 | } 101 | 102 | // Set the connection header if not already specified 103 | NSString *connection = [response headerField:@"Connection"]; 104 | if (!connection) { 105 | connection = [self shouldDie] ? @"close" : @"keep-alive"; 106 | [response setHeaderField:@"Connection" value:connection]; 107 | } 108 | } 109 | 110 | - (NSData *)preprocessResponse:(HTTPMessage *)response { 111 | [self setHeadersForResponse:response isError:NO]; 112 | return [super preprocessResponse:response]; 113 | } 114 | 115 | - (NSData *)preprocessErrorResponse:(HTTPMessage *)response { 116 | [self setHeadersForResponse:response isError:YES]; 117 | return [super preprocessErrorResponse:response]; 118 | } 119 | 120 | - (BOOL)shouldDie { 121 | __block BOOL shouldDie = [super shouldDie]; 122 | 123 | // Allow custom headers to determine if the connection should be closed 124 | if (!shouldDie && headers) { 125 | [headers enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL *stop) { 126 | if ([field caseInsensitiveCompare:@"connection"] == NSOrderedSame) { 127 | if ([value caseInsensitiveCompare:@"close"] == NSOrderedSame) { 128 | shouldDie = YES; 129 | } 130 | *stop = YES; 131 | } 132 | }]; 133 | } 134 | 135 | return shouldDie; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /xFonts/RoutingHTTPServer/RoutingHTTPServer.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "HTTPServer.h" 3 | #import "RouteRequest.h" 4 | #import "RouteResponse.h" 5 | 6 | typedef void (^RequestHandler)(RouteRequest *request, RouteResponse *response); 7 | 8 | @interface RoutingHTTPServer : HTTPServer 9 | 10 | @property (nonatomic, readonly) NSDictionary *defaultHeaders; 11 | 12 | // Specifies headers that will be set on every response. 13 | // These headers can be overridden by RouteResponses. 14 | - (void)setDefaultHeaders:(NSDictionary *)headers; 15 | - (void)setDefaultHeader:(NSString *)field value:(NSString *)value; 16 | 17 | // Returns the dispatch queue on which routes are processed. 18 | // By default this is NULL and routes are processed on CocoaHTTPServer's 19 | // connection queue. You can specify a queue to process routes on, such as 20 | // dispatch_get_main_queue() to process all routes on the main thread. 21 | - (dispatch_queue_t)routeQueue; 22 | - (void)setRouteQueue:(dispatch_queue_t)queue; 23 | 24 | - (NSDictionary *)mimeTypes; 25 | - (void)setMIMETypes:(NSDictionary *)types; 26 | - (void)setMIMEType:(NSString *)type forExtension:(NSString *)ext; 27 | - (NSString *)mimeTypeForPath:(NSString *)path; 28 | 29 | // Convenience methods. Yes I know, this is Cocoa and we don't use convenience 30 | // methods because typing lengthy primitives over and over and over again is 31 | // elegant with the beauty and the poetry. These are just, you know, here. 32 | - (void)get:(NSString *)path withBlock:(RequestHandler)block; 33 | - (void)post:(NSString *)path withBlock:(RequestHandler)block; 34 | - (void)put:(NSString *)path withBlock:(RequestHandler)block; 35 | - (void)delete:(NSString *)path withBlock:(RequestHandler)block; 36 | 37 | - (void)handleMethod:(NSString *)method withPath:(NSString *)path block:(RequestHandler)block; 38 | - (void)handleMethod:(NSString *)method withPath:(NSString *)path target:(id)target selector:(SEL)selector; 39 | 40 | - (BOOL)supportsMethod:(NSString *)method; 41 | - (RouteResponse *)routeMethod:(NSString *)method withPath:(NSString *)path parameters:(NSDictionary *)params request:(HTTPMessage *)request connection:(HTTPConnection *)connection; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /xFonts/TabBarController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarController.h 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/19/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface TabBarController : UITabBarController 14 | 15 | - (void)showHelpOverlay; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /xFonts/TabBarController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarController.m 3 | // xFonts 4 | // 5 | // Created by Craig Hockenberry on 4/19/20. 6 | // Copyright © 2020 manolo. All rights reserved. 7 | // 8 | 9 | #import "TabBarController.h" 10 | 11 | @interface TabBarController () 12 | 13 | @end 14 | 15 | @implementation TabBarController 16 | 17 | - (void)showHelpOverlay 18 | { 19 | [self performSegueWithIdentifier:@"helpOverlay" sender:self]; 20 | } 21 | 22 | - (IBAction)unwindToTabBar:(UIStoryboardSegue *)unwindSegue 23 | { 24 | [self dismissViewControllerAnimated:YES completion:nil]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /xFonts/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // xFonts 4 | // 5 | // Created by manolo on 2/1/17. 6 | // Copyright © 2017 manolo. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | @import CoreText; 11 | 12 | #import "FontInfo.h" 13 | 14 | @interface ViewController : UITableViewController 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /xFonts/iTunes Screen Shot.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/iTunes Screen Shot.psd -------------------------------------------------------------------------------- /xFonts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 109 | 110 | Install 111 | 112 | 113 |

    Fonts Ready to Download

    114 | 115 |

    A configuration profile with your fonts is ready to download into Settings. Tap the button below to begin, then tap Allow when prompted.

    116 | Download Fonts 117 |

    When you see the confirmation alert on this page, open the Settings app and navigate to General > VPN & Device Management to install the $(PRODUCT_NAME) Installation profile.

    118 |

    After Settings are updated, the fonts will appear in other apps like Pages, Keynote, Numbers, and Tot.

    119 | 120 | 121 | -------------------------------------------------------------------------------- /xFonts/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // xFonts 4 | // 5 | // Created by manolo on 2/1/17. 6 | // Copyright © 2017 manolo. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /xFonts/xFonts Icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manolosavi/xFonts/b0d4ee312718b3a78945b06b0215d117904c2006/xFonts/xFonts Icon.sketch -------------------------------------------------------------------------------- /xFonts/xFonts/Base.lproj/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | --------------------------------------------------------------------------------