├── LICENSE ├── Tesseract-Box-Editor.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── erikla.xcuserdatad │ └── xcschemes │ ├── Tesseract-Box-Editor.xcscheme │ └── xcschememanagement.plist ├── Tesseract-Box-Editor ├── Base.lproj │ └── MainMenu.xib ├── Box.swift ├── BoxEditorViewController.swift ├── BoxResizeDelegateProtocol.swift ├── CharacterView.swift ├── Document.swift ├── Document.xib ├── DocumentWindowController.swift ├── ImageView.swift ├── ImageViewWithSelectionRect.swift ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── TBE-1-thumb.jpg ├── TBE-1.jpg ├── TBEAppDelegate.h ├── TBEAppDelegate.m ├── Tesseract Box Editor-Bridging-Header.h ├── Tesseract-Box-Editor-Info.plist ├── Tesseract-Box-Editor-Prefix.pch ├── en.lproj │ ├── Credits.rtf │ └── InfoPlist.strings └── main.m ├── Tesseract-Box-EditorTests ├── Tesseract-Box-EditorTests-Info.plist ├── Tesseract_Box_EditorTests.m └── en.lproj │ └── InfoPlist.strings └── readme.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2014 Erik Larsen. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B34AF637193FDCB400DA19FA /* CharacterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34AF636193FDCB400DA19FA /* CharacterView.swift */; }; 11 | B35EB4A719450D7A0064A62A /* ImageViewWithSelectionRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35EB4A619450D7A0064A62A /* ImageViewWithSelectionRect.swift */; }; 12 | B3831BA9195F3E1100AD552E /* DocumentWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3831BA8195F3E1100AD552E /* DocumentWindowController.swift */; }; 13 | B3839F34195DDCCC00C45821 /* Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = B3839F33195DDCCC00C45821 /* Document.xib */; }; 14 | B3839F36195DE74C00C45821 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3839F35195DE74C00C45821 /* Document.swift */; }; 15 | B39693BD19424052004E13AA /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39693BC19424052004E13AA /* Box.swift */; }; 16 | B39693BF19424084004E13AA /* BoxEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39693BE19424084004E13AA /* BoxEditorViewController.swift */; }; 17 | B39693C11942A0F5004E13AA /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39693C01942A0F5004E13AA /* ImageView.swift */; }; 18 | B3C9261219340283006A1020 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3C9261119340283006A1020 /* Cocoa.framework */; }; 19 | B3C9261C19340283006A1020 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B3C9261A19340283006A1020 /* InfoPlist.strings */; }; 20 | B3C9261E19340283006A1020 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B3C9261D19340283006A1020 /* main.m */; }; 21 | B3C9262219340283006A1020 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = B3C9262019340283006A1020 /* Credits.rtf */; }; 22 | B3C9262519340283006A1020 /* TBEAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B3C9262419340283006A1020 /* TBEAppDelegate.m */; }; 23 | B3C9262819340283006A1020 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = B3C9262619340283006A1020 /* MainMenu.xib */; }; 24 | B3C9262A19340283006A1020 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3C9262919340283006A1020 /* Images.xcassets */; }; 25 | B3C9263119340283006A1020 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3C9263019340283006A1020 /* XCTest.framework */; }; 26 | B3C9263219340283006A1020 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3C9261119340283006A1020 /* Cocoa.framework */; }; 27 | B3C9263A19340283006A1020 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B3C9263819340283006A1020 /* InfoPlist.strings */; }; 28 | B3C9263C19340283006A1020 /* Tesseract_Box_EditorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B3C9263B19340283006A1020 /* Tesseract_Box_EditorTests.m */; }; 29 | B3DB7F61195E131E00ACE379 /* BoxResizeDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3DB7F60195E131E00ACE379 /* BoxResizeDelegateProtocol.swift */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXContainerItemProxy section */ 33 | B3C9263319340283006A1020 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = B3C9260619340282006A1020 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = B3C9260D19340283006A1020; 38 | remoteInfo = "Tesseract-Box-Editor"; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | B34AF635193FDCB400DA19FA /* Tesseract Box Editor-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tesseract Box Editor-Bridging-Header.h"; sourceTree = ""; }; 44 | B34AF636193FDCB400DA19FA /* CharacterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterView.swift; sourceTree = ""; }; 45 | B350FE13195364C800124CA9 /* readme.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = readme.md; sourceTree = SOURCE_ROOT; }; 46 | B350FE141953680400124CA9 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; }; 47 | B35EB4A619450D7A0064A62A /* ImageViewWithSelectionRect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewWithSelectionRect.swift; sourceTree = ""; }; 48 | B3831BA8195F3E1100AD552E /* DocumentWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentWindowController.swift; sourceTree = ""; }; 49 | B3839F33195DDCCC00C45821 /* Document.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = Document.xib; sourceTree = ""; }; 50 | B3839F35195DE74C00C45821 /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = ""; }; 51 | B39693BC19424052004E13AA /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; 52 | B39693BE19424084004E13AA /* BoxEditorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxEditorViewController.swift; sourceTree = ""; }; 53 | B39693C01942A0F5004E13AA /* ImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = ""; }; 54 | B3C9260E19340283006A1020 /* Tesseract Box Editor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Tesseract Box Editor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | B3C9261119340283006A1020 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 56 | B3C9261419340283006A1020 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 57 | B3C9261519340283006A1020 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 58 | B3C9261619340283006A1020 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 59 | B3C9261919340283006A1020 /* Tesseract-Box-Editor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tesseract-Box-Editor-Info.plist"; sourceTree = ""; }; 60 | B3C9261B19340283006A1020 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 61 | B3C9261D19340283006A1020 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 62 | B3C9261F19340283006A1020 /* Tesseract-Box-Editor-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tesseract-Box-Editor-Prefix.pch"; sourceTree = ""; }; 63 | B3C9262119340283006A1020 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 64 | B3C9262319340283006A1020 /* TBEAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TBEAppDelegate.h; sourceTree = ""; }; 65 | B3C9262419340283006A1020 /* TBEAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TBEAppDelegate.m; sourceTree = ""; }; 66 | B3C9262719340283006A1020 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 67 | B3C9262919340283006A1020 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 68 | B3C9262F19340283006A1020 /* Tesseract-Box-EditorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tesseract-Box-EditorTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | B3C9263019340283006A1020 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 70 | B3C9263719340283006A1020 /* Tesseract-Box-EditorTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tesseract-Box-EditorTests-Info.plist"; sourceTree = ""; }; 71 | B3C9263919340283006A1020 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 72 | B3C9263B19340283006A1020 /* Tesseract_Box_EditorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tesseract_Box_EditorTests.m; sourceTree = ""; }; 73 | B3DB7F60195E131E00ACE379 /* BoxResizeDelegateProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxResizeDelegateProtocol.swift; sourceTree = ""; }; 74 | B3EB961219634EAD00AD4EF9 /* TBE-1-thumb.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "TBE-1-thumb.jpg"; sourceTree = ""; }; 75 | B3EB961319634EAD00AD4EF9 /* TBE-1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "TBE-1.jpg"; sourceTree = ""; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | B3C9260B19340283006A1020 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | B3C9261219340283006A1020 /* Cocoa.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | B3C9262C19340283006A1020 /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | B3C9263219340283006A1020 /* Cocoa.framework in Frameworks */, 92 | B3C9263119340283006A1020 /* XCTest.framework in Frameworks */, 93 | ); 94 | runOnlyForDeploymentPostprocessing = 0; 95 | }; 96 | /* End PBXFrameworksBuildPhase section */ 97 | 98 | /* Begin PBXGroup section */ 99 | B3C9260519340282006A1020 = { 100 | isa = PBXGroup; 101 | children = ( 102 | B3C9261719340283006A1020 /* Tesseract-Box-Editor */, 103 | B3C9263519340283006A1020 /* Tesseract-Box-EditorTests */, 104 | B3C9261019340283006A1020 /* Frameworks */, 105 | B3C9260F19340283006A1020 /* Products */, 106 | ); 107 | sourceTree = ""; 108 | }; 109 | B3C9260F19340283006A1020 /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | B3C9260E19340283006A1020 /* Tesseract Box Editor.app */, 113 | B3C9262F19340283006A1020 /* Tesseract-Box-EditorTests.xctest */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | B3C9261019340283006A1020 /* Frameworks */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | B3C9261119340283006A1020 /* Cocoa.framework */, 122 | B3C9263019340283006A1020 /* XCTest.framework */, 123 | B3C9261319340283006A1020 /* Other Frameworks */, 124 | ); 125 | name = Frameworks; 126 | sourceTree = ""; 127 | }; 128 | B3C9261319340283006A1020 /* Other Frameworks */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | B3C9261419340283006A1020 /* AppKit.framework */, 132 | B3C9261519340283006A1020 /* CoreData.framework */, 133 | B3C9261619340283006A1020 /* Foundation.framework */, 134 | ); 135 | name = "Other Frameworks"; 136 | sourceTree = ""; 137 | }; 138 | B3C9261719340283006A1020 /* Tesseract-Box-Editor */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | B3C9262319340283006A1020 /* TBEAppDelegate.h */, 142 | B3C9262419340283006A1020 /* TBEAppDelegate.m */, 143 | B3C9262619340283006A1020 /* MainMenu.xib */, 144 | B34AF636193FDCB400DA19FA /* CharacterView.swift */, 145 | B39693BC19424052004E13AA /* Box.swift */, 146 | B39693BE19424084004E13AA /* BoxEditorViewController.swift */, 147 | B3839F35195DE74C00C45821 /* Document.swift */, 148 | B3831BA8195F3E1100AD552E /* DocumentWindowController.swift */, 149 | B3DB7F60195E131E00ACE379 /* BoxResizeDelegateProtocol.swift */, 150 | B3839F33195DDCCC00C45821 /* Document.xib */, 151 | B35EB4A619450D7A0064A62A /* ImageViewWithSelectionRect.swift */, 152 | B39693C01942A0F5004E13AA /* ImageView.swift */, 153 | B3C9262919340283006A1020 /* Images.xcassets */, 154 | B3C9261819340283006A1020 /* Supporting Files */, 155 | B34AF635193FDCB400DA19FA /* Tesseract Box Editor-Bridging-Header.h */, 156 | B350FE13195364C800124CA9 /* readme.md */, 157 | B350FE141953680400124CA9 /* LICENSE */, 158 | ); 159 | path = "Tesseract-Box-Editor"; 160 | sourceTree = ""; 161 | }; 162 | B3C9261819340283006A1020 /* Supporting Files */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | B3EB961219634EAD00AD4EF9 /* TBE-1-thumb.jpg */, 166 | B3EB961319634EAD00AD4EF9 /* TBE-1.jpg */, 167 | B3C9261919340283006A1020 /* Tesseract-Box-Editor-Info.plist */, 168 | B3C9261A19340283006A1020 /* InfoPlist.strings */, 169 | B3C9261D19340283006A1020 /* main.m */, 170 | B3C9261F19340283006A1020 /* Tesseract-Box-Editor-Prefix.pch */, 171 | B3C9262019340283006A1020 /* Credits.rtf */, 172 | ); 173 | name = "Supporting Files"; 174 | sourceTree = ""; 175 | }; 176 | B3C9263519340283006A1020 /* Tesseract-Box-EditorTests */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | B3C9263B19340283006A1020 /* Tesseract_Box_EditorTests.m */, 180 | B3C9263619340283006A1020 /* Supporting Files */, 181 | ); 182 | path = "Tesseract-Box-EditorTests"; 183 | sourceTree = ""; 184 | }; 185 | B3C9263619340283006A1020 /* Supporting Files */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | B3C9263719340283006A1020 /* Tesseract-Box-EditorTests-Info.plist */, 189 | B3C9263819340283006A1020 /* InfoPlist.strings */, 190 | ); 191 | name = "Supporting Files"; 192 | sourceTree = ""; 193 | }; 194 | /* End PBXGroup section */ 195 | 196 | /* Begin PBXNativeTarget section */ 197 | B3C9260D19340283006A1020 /* Tesseract Box Editor */ = { 198 | isa = PBXNativeTarget; 199 | buildConfigurationList = B3C9263F19340283006A1020 /* Build configuration list for PBXNativeTarget "Tesseract Box Editor" */; 200 | buildPhases = ( 201 | B3C9260A19340283006A1020 /* Sources */, 202 | B3C9260B19340283006A1020 /* Frameworks */, 203 | B3C9260C19340283006A1020 /* Resources */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | ); 209 | name = "Tesseract Box Editor"; 210 | productName = "Tesseract-Box-Editor"; 211 | productReference = B3C9260E19340283006A1020 /* Tesseract Box Editor.app */; 212 | productType = "com.apple.product-type.application"; 213 | }; 214 | B3C9262E19340283006A1020 /* Tesseract-Box-EditorTests */ = { 215 | isa = PBXNativeTarget; 216 | buildConfigurationList = B3C9264219340283006A1020 /* Build configuration list for PBXNativeTarget "Tesseract-Box-EditorTests" */; 217 | buildPhases = ( 218 | B3C9262B19340283006A1020 /* Sources */, 219 | B3C9262C19340283006A1020 /* Frameworks */, 220 | B3C9262D19340283006A1020 /* Resources */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | B3C9263419340283006A1020 /* PBXTargetDependency */, 226 | ); 227 | name = "Tesseract-Box-EditorTests"; 228 | productName = "Tesseract-Box-EditorTests"; 229 | productReference = B3C9262F19340283006A1020 /* Tesseract-Box-EditorTests.xctest */; 230 | productType = "com.apple.product-type.bundle.unit-test"; 231 | }; 232 | /* End PBXNativeTarget section */ 233 | 234 | /* Begin PBXProject section */ 235 | B3C9260619340282006A1020 /* Project object */ = { 236 | isa = PBXProject; 237 | attributes = { 238 | CLASSPREFIX = TBE; 239 | LastSwiftUpdateCheck = 0700; 240 | LastUpgradeCheck = 0700; 241 | ORGANIZATIONNAME = "Erik Larsen"; 242 | TargetAttributes = { 243 | B3C9262E19340283006A1020 = { 244 | TestTargetID = B3C9260D19340283006A1020; 245 | }; 246 | }; 247 | }; 248 | buildConfigurationList = B3C9260919340282006A1020 /* Build configuration list for PBXProject "Tesseract-Box-Editor" */; 249 | compatibilityVersion = "Xcode 3.2"; 250 | developmentRegion = English; 251 | hasScannedForEncodings = 0; 252 | knownRegions = ( 253 | en, 254 | Base, 255 | ); 256 | mainGroup = B3C9260519340282006A1020; 257 | productRefGroup = B3C9260F19340283006A1020 /* Products */; 258 | projectDirPath = ""; 259 | projectRoot = ""; 260 | targets = ( 261 | B3C9260D19340283006A1020 /* Tesseract Box Editor */, 262 | B3C9262E19340283006A1020 /* Tesseract-Box-EditorTests */, 263 | ); 264 | }; 265 | /* End PBXProject section */ 266 | 267 | /* Begin PBXResourcesBuildPhase section */ 268 | B3C9260C19340283006A1020 /* Resources */ = { 269 | isa = PBXResourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | B3C9261C19340283006A1020 /* InfoPlist.strings in Resources */, 273 | B3C9262A19340283006A1020 /* Images.xcassets in Resources */, 274 | B3C9262219340283006A1020 /* Credits.rtf in Resources */, 275 | B3839F34195DDCCC00C45821 /* Document.xib in Resources */, 276 | B3C9262819340283006A1020 /* MainMenu.xib in Resources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | B3C9262D19340283006A1020 /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | B3C9263A19340283006A1020 /* InfoPlist.strings in Resources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXResourcesBuildPhase section */ 289 | 290 | /* Begin PBXSourcesBuildPhase section */ 291 | B3C9260A19340283006A1020 /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | B3DB7F61195E131E00ACE379 /* BoxResizeDelegateProtocol.swift in Sources */, 296 | B3839F36195DE74C00C45821 /* Document.swift in Sources */, 297 | B39693C11942A0F5004E13AA /* ImageView.swift in Sources */, 298 | B35EB4A719450D7A0064A62A /* ImageViewWithSelectionRect.swift in Sources */, 299 | B3831BA9195F3E1100AD552E /* DocumentWindowController.swift in Sources */, 300 | B39693BF19424084004E13AA /* BoxEditorViewController.swift in Sources */, 301 | B34AF637193FDCB400DA19FA /* CharacterView.swift in Sources */, 302 | B39693BD19424052004E13AA /* Box.swift in Sources */, 303 | B3C9262519340283006A1020 /* TBEAppDelegate.m in Sources */, 304 | B3C9261E19340283006A1020 /* main.m in Sources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | B3C9262B19340283006A1020 /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | B3C9263C19340283006A1020 /* Tesseract_Box_EditorTests.m in Sources */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | /* End PBXSourcesBuildPhase section */ 317 | 318 | /* Begin PBXTargetDependency section */ 319 | B3C9263419340283006A1020 /* PBXTargetDependency */ = { 320 | isa = PBXTargetDependency; 321 | target = B3C9260D19340283006A1020 /* Tesseract Box Editor */; 322 | targetProxy = B3C9263319340283006A1020 /* PBXContainerItemProxy */; 323 | }; 324 | /* End PBXTargetDependency section */ 325 | 326 | /* Begin PBXVariantGroup section */ 327 | B3C9261A19340283006A1020 /* InfoPlist.strings */ = { 328 | isa = PBXVariantGroup; 329 | children = ( 330 | B3C9261B19340283006A1020 /* en */, 331 | ); 332 | name = InfoPlist.strings; 333 | sourceTree = ""; 334 | }; 335 | B3C9262019340283006A1020 /* Credits.rtf */ = { 336 | isa = PBXVariantGroup; 337 | children = ( 338 | B3C9262119340283006A1020 /* en */, 339 | ); 340 | name = Credits.rtf; 341 | sourceTree = ""; 342 | }; 343 | B3C9262619340283006A1020 /* MainMenu.xib */ = { 344 | isa = PBXVariantGroup; 345 | children = ( 346 | B3C9262719340283006A1020 /* Base */, 347 | ); 348 | name = MainMenu.xib; 349 | sourceTree = ""; 350 | }; 351 | B3C9263819340283006A1020 /* InfoPlist.strings */ = { 352 | isa = PBXVariantGroup; 353 | children = ( 354 | B3C9263919340283006A1020 /* en */, 355 | ); 356 | name = InfoPlist.strings; 357 | sourceTree = ""; 358 | }; 359 | /* End PBXVariantGroup section */ 360 | 361 | /* Begin XCBuildConfiguration section */ 362 | B3C9263D19340283006A1020 /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | ALWAYS_SEARCH_USER_PATHS = NO; 366 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 367 | CLANG_CXX_LIBRARY = "libc++"; 368 | CLANG_ENABLE_MODULES = YES; 369 | CLANG_ENABLE_OBJC_ARC = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_CONSTANT_CONVERSION = YES; 372 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 373 | CLANG_WARN_EMPTY_BODY = YES; 374 | CLANG_WARN_ENUM_CONVERSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 377 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 378 | COPY_PHASE_STRIP = NO; 379 | ENABLE_TESTABILITY = YES; 380 | GCC_C_LANGUAGE_STANDARD = gnu99; 381 | GCC_DYNAMIC_NO_PIC = NO; 382 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 383 | GCC_OPTIMIZATION_LEVEL = 0; 384 | GCC_PREPROCESSOR_DEFINITIONS = ( 385 | "DEBUG=1", 386 | "$(inherited)", 387 | ); 388 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 389 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 390 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 391 | GCC_WARN_UNDECLARED_SELECTOR = YES; 392 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 393 | GCC_WARN_UNUSED_FUNCTION = YES; 394 | GCC_WARN_UNUSED_VARIABLE = YES; 395 | MACOSX_DEPLOYMENT_TARGET = 10.9; 396 | ONLY_ACTIVE_ARCH = YES; 397 | PRODUCT_MODULE_NAME = TesseractBoxEditor; 398 | SDKROOT = macosx; 399 | }; 400 | name = Debug; 401 | }; 402 | B3C9263E19340283006A1020 /* Release */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | ALWAYS_SEARCH_USER_PATHS = NO; 406 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 407 | CLANG_CXX_LIBRARY = "libc++"; 408 | CLANG_ENABLE_MODULES = YES; 409 | CLANG_ENABLE_OBJC_ARC = YES; 410 | CLANG_WARN_BOOL_CONVERSION = YES; 411 | CLANG_WARN_CONSTANT_CONVERSION = YES; 412 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 413 | CLANG_WARN_EMPTY_BODY = YES; 414 | CLANG_WARN_ENUM_CONVERSION = YES; 415 | CLANG_WARN_INT_CONVERSION = YES; 416 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 418 | COPY_PHASE_STRIP = YES; 419 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 420 | ENABLE_NS_ASSERTIONS = NO; 421 | GCC_C_LANGUAGE_STANDARD = gnu99; 422 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 424 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 425 | GCC_WARN_UNDECLARED_SELECTOR = YES; 426 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 427 | GCC_WARN_UNUSED_FUNCTION = YES; 428 | GCC_WARN_UNUSED_VARIABLE = YES; 429 | MACOSX_DEPLOYMENT_TARGET = 10.9; 430 | PRODUCT_MODULE_NAME = TesseractBoxEditor; 431 | SDKROOT = macosx; 432 | }; 433 | name = Release; 434 | }; 435 | B3C9264019340283006A1020 /* Debug */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | CLANG_ENABLE_MODULES = YES; 440 | COMBINE_HIDPI_IMAGES = YES; 441 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 442 | GCC_PREFIX_HEADER = "Tesseract-Box-Editor/Tesseract-Box-Editor-Prefix.pch"; 443 | INFOPLIST_FILE = "Tesseract-Box-Editor/Tesseract-Box-Editor-Info.plist"; 444 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 445 | PRODUCT_BUNDLE_IDENTIFIER = "com.enlarsen.${PRODUCT_NAME:rfc1034identifier}"; 446 | PRODUCT_MODULE_NAME = TesseractBoxEditor; 447 | PRODUCT_NAME = "$(TARGET_NAME)"; 448 | SWIFT_OBJC_BRIDGING_HEADER = "Tesseract-Box-Editor/Tesseract Box Editor-Bridging-Header.h"; 449 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 450 | WRAPPER_EXTENSION = app; 451 | }; 452 | name = Debug; 453 | }; 454 | B3C9264119340283006A1020 /* Release */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 458 | CLANG_ENABLE_MODULES = YES; 459 | COMBINE_HIDPI_IMAGES = YES; 460 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 461 | GCC_PREFIX_HEADER = "Tesseract-Box-Editor/Tesseract-Box-Editor-Prefix.pch"; 462 | INFOPLIST_FILE = "Tesseract-Box-Editor/Tesseract-Box-Editor-Info.plist"; 463 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 464 | PRODUCT_BUNDLE_IDENTIFIER = "com.enlarsen.${PRODUCT_NAME:rfc1034identifier}"; 465 | PRODUCT_MODULE_NAME = TesseractBoxEditor; 466 | PRODUCT_NAME = "$(TARGET_NAME)"; 467 | SWIFT_OBJC_BRIDGING_HEADER = "Tesseract-Box-Editor/Tesseract Box Editor-Bridging-Header.h"; 468 | WRAPPER_EXTENSION = app; 469 | }; 470 | name = Release; 471 | }; 472 | B3C9264319340283006A1020 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Tesseract-Box-Editor.app/Contents/MacOS/Tesseract-Box-Editor"; 476 | COMBINE_HIDPI_IMAGES = YES; 477 | FRAMEWORK_SEARCH_PATHS = ( 478 | "$(DEVELOPER_FRAMEWORKS_DIR)", 479 | "$(inherited)", 480 | ); 481 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 482 | GCC_PREFIX_HEADER = "Tesseract-Box-Editor/Tesseract-Box-Editor-Prefix.pch"; 483 | GCC_PREPROCESSOR_DEFINITIONS = ( 484 | "DEBUG=1", 485 | "$(inherited)", 486 | ); 487 | INFOPLIST_FILE = "Tesseract-Box-EditorTests/Tesseract-Box-EditorTests-Info.plist"; 488 | PRODUCT_BUNDLE_IDENTIFIER = "com.enlarsen.${PRODUCT_NAME:rfc1034identifier}"; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | TEST_HOST = "$(BUNDLE_LOADER)"; 491 | WRAPPER_EXTENSION = xctest; 492 | }; 493 | name = Debug; 494 | }; 495 | B3C9264419340283006A1020 /* Release */ = { 496 | isa = XCBuildConfiguration; 497 | buildSettings = { 498 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Tesseract-Box-Editor.app/Contents/MacOS/Tesseract-Box-Editor"; 499 | COMBINE_HIDPI_IMAGES = YES; 500 | FRAMEWORK_SEARCH_PATHS = ( 501 | "$(DEVELOPER_FRAMEWORKS_DIR)", 502 | "$(inherited)", 503 | ); 504 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 505 | GCC_PREFIX_HEADER = "Tesseract-Box-Editor/Tesseract-Box-Editor-Prefix.pch"; 506 | INFOPLIST_FILE = "Tesseract-Box-EditorTests/Tesseract-Box-EditorTests-Info.plist"; 507 | PRODUCT_BUNDLE_IDENTIFIER = "com.enlarsen.${PRODUCT_NAME:rfc1034identifier}"; 508 | PRODUCT_NAME = "$(TARGET_NAME)"; 509 | TEST_HOST = "$(BUNDLE_LOADER)"; 510 | WRAPPER_EXTENSION = xctest; 511 | }; 512 | name = Release; 513 | }; 514 | /* End XCBuildConfiguration section */ 515 | 516 | /* Begin XCConfigurationList section */ 517 | B3C9260919340282006A1020 /* Build configuration list for PBXProject "Tesseract-Box-Editor" */ = { 518 | isa = XCConfigurationList; 519 | buildConfigurations = ( 520 | B3C9263D19340283006A1020 /* Debug */, 521 | B3C9263E19340283006A1020 /* Release */, 522 | ); 523 | defaultConfigurationIsVisible = 0; 524 | defaultConfigurationName = Release; 525 | }; 526 | B3C9263F19340283006A1020 /* Build configuration list for PBXNativeTarget "Tesseract Box Editor" */ = { 527 | isa = XCConfigurationList; 528 | buildConfigurations = ( 529 | B3C9264019340283006A1020 /* Debug */, 530 | B3C9264119340283006A1020 /* Release */, 531 | ); 532 | defaultConfigurationIsVisible = 0; 533 | defaultConfigurationName = Release; 534 | }; 535 | B3C9264219340283006A1020 /* Build configuration list for PBXNativeTarget "Tesseract-Box-EditorTests" */ = { 536 | isa = XCConfigurationList; 537 | buildConfigurations = ( 538 | B3C9264319340283006A1020 /* Debug */, 539 | B3C9264419340283006A1020 /* Release */, 540 | ); 541 | defaultConfigurationIsVisible = 0; 542 | defaultConfigurationName = Release; 543 | }; 544 | /* End XCConfigurationList section */ 545 | }; 546 | rootObject = B3C9260619340282006A1020 /* Project object */; 547 | } 548 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor.xcodeproj/xcuserdata/erikla.xcuserdatad/xcschemes/Tesseract-Box-Editor.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor.xcodeproj/xcuserdata/erikla.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Tesseract-Box-Editor.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B3C9260D19340283006A1020 16 | 17 | primary 18 | 19 | 20 | B3C9262E19340283006A1020 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 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 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | Default 536 | 537 | 538 | 539 | 540 | 541 | 542 | Left to Right 543 | 544 | 545 | 546 | 547 | 548 | 549 | Right to Left 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | Default 561 | 562 | 563 | 564 | 565 | 566 | 567 | Left to Right 568 | 569 | 570 | 571 | 572 | 573 | 574 | Right to Left 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/6/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | import Foundation 29 | 30 | class Box: NSObject 31 | { 32 | var x: Int = 0 33 | var y: Int = 0 34 | var x2: Int = 0 35 | var y2: Int = 0 36 | var page: Int = 0 37 | var character: String = " " 38 | 39 | var width: Int 40 | { 41 | get 42 | { 43 | return x2 - x 44 | } 45 | set(newValue) 46 | { 47 | x2 = x + newValue 48 | } 49 | } 50 | 51 | var height: Int 52 | { 53 | get 54 | { 55 | return y2 - y 56 | } 57 | set(newValue) 58 | { 59 | y2 = y + newValue 60 | } 61 | } 62 | 63 | class func boxToNSRect(box: Box) -> NSRect 64 | { 65 | return NSRect(x: box.x, y: box.y, width: box.width, height: box.height) 66 | } 67 | 68 | func boxToNSRect() -> NSRect 69 | { 70 | return Box.boxToNSRect(self) 71 | } 72 | 73 | func formatForWriting() -> String 74 | { 75 | return NSString(format: "%@ %d %d %d %d %d\n", character, 76 | x, y, x2, y2, page) as String 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/BoxEditorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BoxEditorViewController.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/6/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | import Foundation 29 | import Cocoa 30 | import QuartzCore 31 | 32 | class BoxEditorViewController: NSViewController, NSWindowDelegate 33 | { 34 | override func awakeFromNib() 35 | { 36 | // self.window.nextResponder = self; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/BoxResizeDelegateProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BoxResizeDelegateProtocol.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/27/14. 6 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol BoxResizeDelegate 12 | { 13 | func boxDidResize(rect: NSRect) 14 | func doneDragging() 15 | func beganDragging() 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/CharacterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharacterView.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/4/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | import Cocoa 29 | import QuartzCore 30 | 31 | class CharacterView: ImageViewWithSelectionRect 32 | { 33 | 34 | var width: CGFloat = 0.0 35 | var height:CGFloat = 0.0 36 | var startPointIndex = -1 37 | var scaleFactor = 1.0 38 | var delegate: BoxResizeDelegate? = nil 39 | 40 | override init(frame frameRect: NSRect) 41 | { 42 | super.init(frame: frameRect) 43 | strokeColor = NSColor.grayColor().CGColor 44 | fillColor = NSColor.clearColor().CGColor 45 | lineDashPattern = [1, 1] 46 | duration = 10.0 47 | } 48 | 49 | required init?(coder: NSCoder) { 50 | fatalError("init(coder:) has not been implemented") 51 | } 52 | 53 | override func awakeFromNib() 54 | { 55 | imageScaling = .ScaleProportionallyUpOrDown 56 | drawSelectionHandles = true 57 | 58 | } 59 | 60 | 61 | func propagateValue(id: AnyObject, binding:NSString) 62 | { 63 | if let bindingInfo = self.infoForBinding(binding as String) 64 | { 65 | if let bindingOptions : AnyObject = bindingInfo[NSOptionsKey] 66 | { 67 | let transformer: NSValueTransformer = bindingOptions[NSValueTransformerBindingOption] as! NSValueTransformer 68 | } 69 | } 70 | else 71 | { 72 | return 73 | } 74 | 75 | } 76 | 77 | func updateCharacter(image: NSImage, cropPoint: NSPoint, rect: NSRect) 78 | { 79 | self.image = image 80 | 81 | self.cropPoint = cropPoint 82 | 83 | self.selectionRect = rect 84 | removeAnimatedSelection() 85 | setupAnimatedSelectionRect(self.selectionRect) 86 | 87 | // NSLog("image size: %@", NSStringFromSize(image.size)); 88 | // NSLog("characterView frame: %@", NSStringFromRect(self.frame)); 89 | // NSLog("crop point: %@", NSStringFromPoint(cropPoint)); 90 | 91 | width = image.size.width 92 | height = image.size.height 93 | 94 | } 95 | 96 | override func mouseDown(theEvent: NSEvent) 97 | { 98 | let point = convertPoint(theEvent.locationInWindow, fromView: nil) 99 | var transform = CGAffineTransformInvert(CATransform3DGetAffineTransform(selectionLayer.transform)) 100 | 101 | for var i = 0; i < selectionHandleLayers.count; i++ 102 | { 103 | if CGPathContainsPoint(selectionHandleLayers[i].path, &transform, point, false) 104 | { 105 | self.startPointIndex = i 106 | break 107 | } 108 | 109 | } 110 | if (delegate != nil) 111 | { 112 | delegate!.beganDragging() 113 | } 114 | 115 | // TODO: also check whether the click was within the selection rectangle in prep for a move 116 | } 117 | 118 | override func mouseUp(theEvent: NSEvent) 119 | { 120 | startPointIndex = -1 121 | 122 | if (delegate != nil) 123 | { 124 | delegate!.doneDragging() 125 | } 126 | } 127 | 128 | override func mouseDragged(theEvent: NSEvent) 129 | { 130 | var point = convertPoint(theEvent.locationInWindow, fromView: nil) 131 | let transform = CGAffineTransformInvert(CATransform3DGetAffineTransform(selectionLayer.transform)) 132 | point = CGPointApplyAffineTransform(point, transform) 133 | 134 | let newRect = computeResizedSelectionRectangle(self.startPointIndex, dragPoint: point) 135 | // NSLog("newRect: \(newRect)") 136 | drawSelectionRect(newRect) 137 | drawHandles(newRect) 138 | 139 | selectionRect = newRect 140 | // window.documentEdited = true 141 | if (delegate != nil) 142 | { 143 | delegate!.boxDidResize(newRect) 144 | } 145 | 146 | } 147 | 148 | } -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Document.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Document.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/27/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | import Foundation 29 | import Cocoa 30 | import QuartzCore 31 | 32 | class Document: NSDocument 33 | { 34 | var pagesFromImage: [NSBitmapImageRep] = [] 35 | var boxes: [Box] = [] 36 | var pageIndex = Dictionary() 37 | 38 | 39 | override var windowNibName: String 40 | { 41 | return "Document" 42 | } 43 | 44 | // TODO: This needs vastly improved error handling and value checking 45 | func readBoxFile(path: String) 46 | { 47 | var error: NSError? = nil 48 | var boxes: [Box] = [] 49 | let fileText: NSString? 50 | do { 51 | fileText = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) 52 | } catch var error1 as NSError { 53 | error = error1 54 | fileText = nil 55 | } 56 | 57 | if let mError = error 58 | { 59 | NSLog("Error: \(mError.localizedDescription)") 60 | } 61 | 62 | fileText!.enumerateLinesUsingBlock({line, stop in 63 | let box = Box() 64 | var intValue: CInt = 0 65 | var characterAsString: NSString? 66 | 67 | let scanner = NSScanner(string: line) 68 | scanner.caseSensitive = true 69 | scanner.charactersToBeSkipped = nil 70 | 71 | scanner.scanUpToString(" ", intoString: &characterAsString) 72 | 73 | if let character = characterAsString 74 | { 75 | box.character = character as String 76 | } 77 | 78 | scanner.charactersToBeSkipped = NSCharacterSet.whitespaceCharacterSet() 79 | 80 | box.x = self.getNextIntValue(scanner) 81 | box.y = self.getNextIntValue(scanner) 82 | box.x2 = self.getNextIntValue(scanner) 83 | box.y2 = self.getNextIntValue(scanner) 84 | box.page = self.getNextIntValue(scanner) 85 | boxes.append(box) 86 | }) 87 | self.boxes = boxes 88 | 89 | } 90 | 91 | 92 | func getNextIntValue(scanner: NSScanner) -> Int 93 | { 94 | var intValue: CInt = 0 95 | 96 | scanner.scanInt(&intValue) 97 | return Int(intValue) 98 | } 99 | 100 | func createPageIndex() 101 | { 102 | pageIndex.removeAll(keepCapacity: true) 103 | var current = -1 104 | 105 | for var i = 0; i < boxes.count; i++ 106 | { 107 | if current != boxes[i].page 108 | { 109 | current = boxes[i].page 110 | pageIndex[current] = i 111 | } 112 | } 113 | 114 | } 115 | 116 | override func readFromURL(url: NSURL, ofType typeName: String) throws 117 | { 118 | readBoxFile(url.path!) 119 | } 120 | 121 | override func writeToURL(url: NSURL, ofType typeName: String) throws 122 | { 123 | var outError: NSError! = NSError(domain: "Migrator", code: 0, userInfo: nil) 124 | var output = "" 125 | 126 | for box in boxes 127 | { 128 | output = output.stringByAppendingString(box.formatForWriting()) 129 | } 130 | 131 | do { 132 | try output.writeToFile(url.path!, atomically: true, encoding: NSUTF8StringEncoding) 133 | } catch var error as NSError { 134 | outError = error 135 | } 136 | 137 | // NSLog("\(outError.memory?.localizedDescription)") 138 | 139 | if outError == nil 140 | { 141 | return 142 | } 143 | else 144 | { 145 | throw outError 146 | } 147 | } 148 | 149 | override func makeWindowControllers() 150 | { 151 | let windowController = DocumentWindowController(windowNibName: self.windowNibName) 152 | addWindowController(windowController) 153 | } 154 | 155 | 156 | } 157 | 158 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Document.xib: -------------------------------------------------------------------------------- 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 | 48 | 58 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 196 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | IA 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | IA 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | IA 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | IA 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | IA 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | character 450 | x 451 | y 452 | width 453 | height 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/DocumentWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DocumentWindowController.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/28/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | 29 | import Cocoa 30 | import QuartzCore 31 | 32 | class DocumentWindowController: NSWindowController, BoxResizeDelegate 33 | { 34 | @IBOutlet var mainImageView: ImageView! 35 | @IBOutlet var characterView: CharacterView! 36 | @IBOutlet var tableArrayController: NSArrayController! 37 | @IBOutlet var tableView: NSTableView! 38 | 39 | override var windowNibName: String 40 | { 41 | return "Document" 42 | } 43 | 44 | var currentDocument: Document 45 | { 46 | get 47 | { 48 | return document as! Document 49 | } 50 | } 51 | 52 | // Used by Cocoa bindings because they won't take a key path to "currentDocument.boxes" 53 | var boxes: [Box] 54 | { 55 | get 56 | { 57 | return currentDocument.boxes 58 | } 59 | set(newValue) 60 | { 61 | currentDocument.boxes = newValue 62 | } 63 | } 64 | 65 | var selectionLayer: CAShapeLayer! 66 | var selectionHandleLayers: [CAShapeLayer] = [] 67 | 68 | var observing = false 69 | 70 | var pagesFromImage: [NSBitmapImageRep] = [] 71 | var currentTiffPage: Int = -1 72 | { 73 | willSet 74 | { 75 | self.willChangeValueForKey("isThereAPreviousPage") 76 | self.willChangeValueForKey("isThereANextPage") 77 | } 78 | didSet 79 | { 80 | self.didChangeValueForKey("isThereAPreviousPage") 81 | self.didChangeValueForKey("isThereANextPage") 82 | } 83 | } 84 | 85 | var isThereAPreviousPage: Bool 86 | { 87 | get 88 | { 89 | if currentTiffPage - 1 < 0 90 | { 91 | return false 92 | } 93 | else 94 | { 95 | return true 96 | } 97 | } 98 | } 99 | 100 | var isThereANextPage: Bool 101 | { 102 | get 103 | { 104 | if currentTiffPage + 1 >= pagesFromImage.count 105 | { 106 | return false 107 | } 108 | else 109 | { 110 | return true 111 | } 112 | } 113 | } 114 | 115 | 116 | override class func automaticallyNotifiesObserversForKey(key: String) -> Bool 117 | { 118 | 119 | if key == "isThereAPreviousPage" || key == "isThereANextPage" 120 | { 121 | return false 122 | } 123 | else 124 | { 125 | return true 126 | } 127 | 128 | } 129 | 130 | 131 | override func awakeFromNib() 132 | { 133 | mainImageView.imageScaling = .ScaleProportionallyUpOrDown 134 | characterView.delegate = self 135 | } 136 | 137 | override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject : AnyObject]?, context: UnsafeMutablePointer) 138 | { 139 | if tableArrayController.selectedObjects.count > 0 140 | { 141 | updateSelectedCharacterDisplays() 142 | } 143 | else 144 | { 145 | mainImageView.removeAnimatedSelection() 146 | } 147 | } 148 | 149 | 150 | func updateSelectedCharacterDisplays() 151 | { 152 | 153 | if tableArrayController?.selectedObjects?.count > 0 154 | { 155 | if let box = tableArrayController?.selectedObjects[0] as? Box 156 | { 157 | if box.page != currentTiffPage 158 | { 159 | if currentTiffPage < pagesFromImage.count 160 | { 161 | let size = pagesFromImage[box.page].size 162 | let image = NSImage(size:size) 163 | image.addRepresentation(pagesFromImage[box.page]) 164 | mainImageView.trimImage(image) 165 | currentTiffPage = box.page 166 | 167 | } 168 | } 169 | 170 | updateCharacterView(box) 171 | mainImageView.removeAnimatedSelection() 172 | mainImageView.setupAnimatedSelectionRect(box.boxToNSRect()) 173 | } 174 | } 175 | 176 | } 177 | 178 | // Someday do everything as straight up bindings. 179 | // Problems with direct binding: need image and crop as well as the box down in the CharacterView, 180 | // unless I move the image down to the viewer classes and then let them crop the image. 181 | func resizeBox(rect: NSRect, index: Int) 182 | { 183 | let box = boxes[index] 184 | box.x = Int(rect.origin.x) 185 | box.y = Int(rect.origin.y) 186 | box.width = Int(rect.size.width) 187 | box.height = Int(rect.size.height) 188 | if self.window!.undoManager!.undoing && index == tableArrayController.selectionIndex 189 | { 190 | updateSelectedCharacterDisplays() 191 | } 192 | } 193 | 194 | func changeCharacter(char: String, index: Int) 195 | { 196 | let box = boxes[index] 197 | self.window!.undoManager!.prepareWithInvocationTarget(self).changeCharacter(box.character, index: index) 198 | if !self.window!.undoManager!.undoing 199 | { 200 | self.window!.undoManager!.setActionName("Change \"\(box.character)\" to \"\(char)\"") 201 | } 202 | box.character = char 203 | } 204 | 205 | func boxDidResize(rect: NSRect) 206 | { 207 | let selectionIndex = tableArrayController.selectionIndex 208 | resizeBox(rect, index: selectionIndex) 209 | } 210 | 211 | func beganDragging() 212 | { 213 | let box = tableArrayController.selectedObjects[0] as! Box 214 | let selectionIndex = tableArrayController.selectionIndex 215 | let currentRect = box.boxToNSRect() 216 | 217 | self.window!.undoManager!.prepareWithInvocationTarget(self).resizeBox(currentRect, index: selectionIndex) 218 | if !self.window!.undoManager!.undoing 219 | { 220 | self.window!.undoManager!.setActionName("Resize Box") 221 | } 222 | 223 | } 224 | 225 | func doneDragging() 226 | { 227 | updateSelectedCharacterDisplays() 228 | } 229 | 230 | func insertBox(box: Box, index: Int) 231 | { 232 | self.window!.undoManager!.prepareWithInvocationTarget(self).removeBox(index) 233 | 234 | if !self.window!.undoManager!.undoing 235 | { 236 | self.window!.undoManager!.setActionName("Insert Box") 237 | } 238 | boxes.insert(box, atIndex: index) 239 | } 240 | 241 | func removeBox(index: Int) 242 | { 243 | let box = boxes[index] 244 | self.window!.undoManager!.prepareWithInvocationTarget(self).insertBox(box, index: index) 245 | 246 | if !self.window!.undoManager!.undoing 247 | { 248 | self.window!.undoManager!.setActionName("Delete Box") 249 | } 250 | boxes.removeAtIndex(index) 251 | 252 | } 253 | 254 | func mergeBoxes(index: Int) 255 | { 256 | let firstBox = boxes[index] 257 | 258 | if index + 1 < boxes.count 259 | { 260 | let secondBox = boxes[index + 1] 261 | self.window!.undoManager!.beginUndoGrouping() 262 | 263 | self.window!.undoManager!.prepareWithInvocationTarget(self).insertBox(secondBox, index: index + 1) 264 | self.window!.undoManager!.prepareWithInvocationTarget(self).resizeBox(firstBox.boxToNSRect(), index: index) 265 | 266 | if !self.window!.undoManager!.undoing 267 | { 268 | self.window!.undoManager!.setActionName("Merge Boxes") 269 | } 270 | self.window!.undoManager!.endUndoGrouping() 271 | 272 | // This is a simplistic merge. Should create a rectangle that encloses both characters, but 273 | // have to test whether the character is at the end of the line and do something reasonable then. 274 | firstBox.width += secondBox.width 275 | removeBox(index + 1) 276 | currentDocument.createPageIndex() 277 | updateSelectedCharacterDisplays() 278 | } 279 | else 280 | { 281 | return 282 | } 283 | 284 | } 285 | 286 | func splitBoxes(index: Int) 287 | { 288 | let box = boxes[index] 289 | 290 | self.window!.undoManager!.beginUndoGrouping() 291 | 292 | self.window!.undoManager!.prepareWithInvocationTarget(self).removeBox(index + 1) 293 | self.window!.undoManager!.prepareWithInvocationTarget(self).resizeBox(box.boxToNSRect(), index:index) 294 | 295 | if !self.window!.undoManager!.undoing 296 | { 297 | self.window!.undoManager!.setActionName("Split Box") 298 | } 299 | self.window!.undoManager!.endUndoGrouping() 300 | 301 | let newBox = Box() 302 | newBox.page = box.page 303 | newBox.character = "?" 304 | newBox.y = box.y 305 | newBox.y2 = box.y2 306 | newBox.x = Int(box.x + (box.width / 2)) 307 | newBox.x2 = box.x2 308 | box.x2 = newBox.x - 2 309 | 310 | boxes.insert(newBox, atIndex: index + 1) 311 | 312 | currentDocument.createPageIndex() 313 | updateSelectedCharacterDisplays() 314 | 315 | } 316 | 317 | @IBAction func mergeToolbarItem(sender: NSToolbarItem) 318 | { 319 | 320 | mergeBoxes(tableArrayController.selectionIndex) 321 | } 322 | 323 | @IBAction func splitToolbarItem(sender: NSToolbarItem) 324 | { 325 | splitBoxes(tableArrayController.selectionIndex) 326 | } 327 | 328 | func deleteToolbarItem(sender: NSToolbarItem) 329 | { 330 | removeBox(tableArrayController.selectionIndex) 331 | } 332 | 333 | func insertToolbarItem(sender: NSToolbarItem) 334 | { 335 | let index = tableArrayController.selectionIndex 336 | 337 | let selectedBox = boxes[index] 338 | 339 | let box = Box() 340 | box.x = selectedBox.x - selectedBox.width 341 | box.y = selectedBox.y 342 | box.width = selectedBox.width 343 | box.height = selectedBox.height 344 | box.character = "?" 345 | box.page = selectedBox.page 346 | 347 | insertBox(box, index: index) 348 | } 349 | 350 | // The KVO will see the change in selection and update the image view 351 | @IBAction func previousPage(sender: NSButton) 352 | { 353 | let index = currentTiffPage - 1 354 | if index < 0 355 | { 356 | return 357 | } 358 | let row = currentDocument.pageIndex[index] 359 | tableArrayController.setSelectionIndex(row!) 360 | updateSelectedCharacterDisplays() 361 | tableView.scrollRowToVisible(row!) 362 | 363 | 364 | } 365 | 366 | @IBAction func nextPage(sender: NSButton) 367 | { 368 | let index = currentTiffPage + 1 369 | if index >= boxes.count 370 | { 371 | return 372 | } 373 | let row = currentDocument.pageIndex[index] 374 | tableArrayController.setSelectionIndex(row!) 375 | updateSelectedCharacterDisplays() 376 | tableView.scrollRowToVisible(row!) 377 | 378 | } 379 | 380 | func scanRow(y: Int, width:Int, pointer: UnsafePointer) -> Bool 381 | { 382 | for var x = 0; x < width; x++ 383 | { 384 | if pointer[(x + y * width) * 4] != 0xff // only check red, could cause trouble 385 | { 386 | return true 387 | } 388 | } 389 | return false 390 | } 391 | 392 | func scanColumn(x: Int, height: Int, width: Int, pointer: UnsafePointer) -> Bool 393 | { 394 | for var y = 0; y < height; y++ 395 | { 396 | if pointer[(x + y * width) * 4] != 0xff // only check red 397 | { 398 | return true 399 | } 400 | } 401 | return false 402 | } 403 | 404 | func updateCharacterView(box: Box) 405 | { 406 | if box.page < pagesFromImage.count 407 | { 408 | let image = NSImage(size: pagesFromImage[box.page].size) 409 | image.addRepresentation(pagesFromImage[box.page]) 410 | 411 | let croppedImage = NSImage(size: NSSize(width: box.width + 10, height: box.height + 10)) 412 | 413 | croppedImage.lockFocus() 414 | NSGraphicsContext.saveGraphicsState() 415 | NSGraphicsContext.currentContext()?.imageInterpolation = NSImageInterpolation.None 416 | NSGraphicsContext.currentContext()?.shouldAntialias = false 417 | 418 | image.drawInRect(NSRect(x: 0, y: 0, width: box.width + 10, height: box.height + 10), 419 | fromRect: NSRect(x: box.x - 5, y: box.y - 5, 420 | width: box.width + 10, height: box.height + 10), 421 | operation: NSCompositingOperation.CompositeCopy, 422 | fraction: 1.0) 423 | 424 | NSGraphicsContext.restoreGraphicsState() 425 | croppedImage.unlockFocus() 426 | 427 | characterView.updateCharacter(croppedImage, cropPoint: NSPoint(x: box.x - 5, y: box.y - 5), rect: box.boxToNSRect()) 428 | } 429 | } 430 | 431 | 432 | func windowDidResize(notification: NSNotification!) 433 | { 434 | if mainImageView.image != nil 435 | { 436 | updateSelectedCharacterDisplays() 437 | } 438 | } 439 | 440 | override func windowDidLoad() 441 | { 442 | super.windowDidLoad() 443 | 444 | if observing 445 | { 446 | self.tableArrayController.removeObserver(self, forKeyPath: "selection") 447 | observing = false 448 | } 449 | 450 | if let tiffUrl = currentDocument.fileURL?.URLByDeletingPathExtension!.URLByAppendingPathExtension("tif") 451 | { 452 | let imageFromFile = NSImage(byReferencingURL: tiffUrl) 453 | pagesFromImage = imageFromFile.representations as! [NSBitmapImageRep] 454 | mainImageView.trimImage(imageFromFile) 455 | currentTiffPage = 0 456 | 457 | tableArrayController.addObserver(self, forKeyPath: "selection", options: [], context: nil) 458 | observing = true 459 | tableArrayController.setSelectionIndex(1) 460 | tableArrayController.setSelectionIndex(0) // Move the selection so the observer sees the change and updates the display 461 | currentDocument.createPageIndex() 462 | tableView.scrollRowToVisible(0) 463 | tableView.becomeFirstResponder() 464 | } 465 | 466 | } 467 | 468 | // TODO: Need to allow composed characters 469 | // TODO: Investigate the method for interpreting characters, see orange book 470 | override func keyDown(theEvent: NSEvent) 471 | { 472 | self.interpretKeyEvents([theEvent]) 473 | 474 | } 475 | 476 | // function for interpretKeyEvents 477 | override func insertText(insertString: AnyObject) 478 | { 479 | if let characters = insertString as? String 480 | { 481 | let selectedIndex = tableArrayController.selectionIndex 482 | changeCharacter(characters, index: selectedIndex) 483 | } 484 | } 485 | 486 | 487 | override func encodeRestorableStateWithCoder(coder: NSCoder) 488 | { 489 | super.encodeRestorableStateWithCoder(coder) 490 | coder.encodeInteger(tableArrayController.selectionIndex, forKey: "selectionIndex") 491 | 492 | 493 | } 494 | 495 | override func restoreStateWithCoder(coder: NSCoder) 496 | { 497 | super.restoreStateWithCoder(coder) 498 | let index = coder.decodeIntegerForKey("selectionIndex") 499 | 500 | tableArrayController.setSelectionIndex(index) 501 | tableView.scrollRowToVisible(index) 502 | updateSelectedCharacterDisplays() 503 | } 504 | } -------------------------------------------------------------------------------- /Tesseract-Box-Editor/ImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageView.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/6/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | import Foundation 29 | import Cocoa 30 | 31 | class ImageView: ImageViewWithSelectionRect 32 | { 33 | 34 | override init(frame frameRect: NSRect) 35 | { 36 | super.init(frame: frameRect) 37 | self.fillColor = NSColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 0.5).CGColor 38 | self.strokeColor = NSColor.redColor().CGColor 39 | self.duration = 4.0 40 | 41 | self.lineDashPattern = [2, 1] 42 | } 43 | 44 | required init?(coder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /Tesseract-Box-Editor/ImageViewWithSelectionRect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViewWithSelectionRect.swift 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 6/8/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | import Foundation 29 | import Cocoa 30 | import QuartzCore 31 | 32 | // Base class for CharacterView and ImageView 33 | 34 | class ImageViewWithSelectionRect: NSImageView 35 | { 36 | var selectionLayer: CAShapeLayer! 37 | var selectionHandleLayers: [CAShapeLayer] = [] 38 | var drawSelectionHandles = false 39 | 40 | var cropPoint = CGPointZero 41 | 42 | var selectionRect = NSZeroRect 43 | var strokeColor = NSColor.blackColor().CGColor 44 | var fillColor = NSColor.clearColor().CGColor 45 | var lineDashPattern = [10, 15] 46 | var duration = 0.75 47 | var numberHandles = 4 48 | 49 | func setupAnimatedSelectionRect(rect: NSRect) 50 | { 51 | 52 | selectionLayer = createAnimationLayer() 53 | layer!.addSublayer(selectionLayer) 54 | 55 | selectionLayer.addAnimation(createAnimation(), forKey: "linePhase") 56 | 57 | 58 | selectionLayer.transform = createTransform(self.cropPoint) 59 | 60 | // NSLog("Cropped point: \(cropPoint)") 61 | 62 | if drawSelectionHandles 63 | { 64 | drawHandles(rect) 65 | } 66 | 67 | drawSelectionRect(rect) 68 | return 69 | } 70 | 71 | func drawSelectionRect(rect: NSRect) 72 | { 73 | let path = CGPathCreateMutable() 74 | 75 | CGPathMoveToPoint(path, nil, rect.origin.x, rect.origin.y) 76 | CGPathAddLineToPoint(path, nil, rect.origin.x, rect.origin.y + rect.size.height) 77 | CGPathAddLineToPoint(path, nil, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height) 78 | CGPathAddLineToPoint(path, nil, rect.origin.x + rect.size.width, rect.origin.y) 79 | CGPathCloseSubpath(path) 80 | 81 | selectionLayer.path = path 82 | } 83 | 84 | 85 | func createAnimationLayer() -> CAShapeLayer 86 | { 87 | let shapeLayer = CAShapeLayer() 88 | shapeLayer.lineWidth = 0.5 89 | shapeLayer.strokeColor = strokeColor 90 | shapeLayer.fillColor = fillColor 91 | shapeLayer.lineDashPattern = lineDashPattern 92 | 93 | return shapeLayer 94 | 95 | } 96 | 97 | func createAnimation() -> CABasicAnimation 98 | { 99 | let dashAnimation = CABasicAnimation(keyPath: "lineDashPhase") 100 | dashAnimation.fromValue = 0.0 101 | dashAnimation.toValue = 15.0 102 | dashAnimation.duration = duration 103 | dashAnimation.cumulative = true 104 | dashAnimation.repeatCount = HUGE 105 | 106 | return dashAnimation 107 | } 108 | 109 | 110 | func createTransform(cropPoint: NSPoint) -> CATransform3D 111 | { 112 | var verticalPadding: CGFloat = 0.0 113 | var horizontalPadding: CGFloat = 0.0 114 | var scaleFactor: CGFloat = 1.0 115 | let horizontalScaleFactor = frame.size.width / image!.size.width 116 | let verticalScaleFactor = frame.size.height / image!.size.height 117 | 118 | // NSLog("horizontal scale: \(horizontalScaleFactor) vertical scale: \(verticalScaleFactor)") 119 | 120 | if verticalScaleFactor - horizontalScaleFactor < 0 121 | { 122 | scaleFactor = verticalScaleFactor 123 | 124 | let width = image!.size.width * scaleFactor 125 | horizontalPadding = (frame.size.width - width) / 2.0 126 | } 127 | else 128 | { 129 | scaleFactor = horizontalScaleFactor 130 | 131 | let height = image!.size.height * scaleFactor 132 | verticalPadding = (frame.size.height - height) / 2.0 133 | } 134 | 135 | // NSLog("Horizontal padding: \(horizontalPadding), vertical padding: \(verticalPadding)") 136 | 137 | var transform = CATransform3DIdentity 138 | transform = CATransform3DTranslate(transform, horizontalPadding, verticalPadding, 0.0) 139 | transform = CATransform3DScale(transform, scaleFactor, scaleFactor, 1.0) 140 | transform = CATransform3DTranslate(transform, -cropPoint.x, -cropPoint.y, 0.0) 141 | 142 | // NSLog("Transformation: \(transform.m11) \(transform.m22) \(transform.m41) \(transform.m42)") 143 | 144 | return transform 145 | } 146 | 147 | func drawHandles(rect: NSRect) 148 | { 149 | var handles: [NSPoint] = [] 150 | 151 | 152 | let left = CGFloat(rect.origin.x) 153 | let bottom = CGFloat(rect.origin.y) 154 | let right = CGFloat(rect.origin.x + rect.size.width) 155 | let top = CGFloat(rect.origin.y + rect.size.height) 156 | 157 | handles.append(NSPoint(x: left, y: bottom + (top - bottom) / 2.0)) // left 158 | handles.append(NSPoint(x: right, y: bottom + (top - bottom) / 2.0)) // right 159 | handles.append(NSPoint(x: left + (right - left) / 2.0, y: top)) // top 160 | handles.append(NSPoint(x: (left + (right - left) / 2.0), y: bottom)) // bottom 161 | 162 | if selectionHandleLayers.count == 0 163 | { 164 | setupSelectionHandleLayers() 165 | } 166 | 167 | for var i = 0; i < handles.count; i++ 168 | { 169 | drawHandle(handles[i], layer: selectionHandleLayers[i]) 170 | } 171 | 172 | } 173 | 174 | func drawHandle(point: NSPoint, layer: CAShapeLayer) 175 | { 176 | let size: CGFloat = 0.5 177 | let path = CGPathCreateMutable() 178 | 179 | CGPathMoveToPoint(path, nil, point.x - size, point.y - size) 180 | CGPathAddLineToPoint(path, nil, point.x - size, point.y + size) 181 | CGPathAddLineToPoint(path, nil, point.x + size, point.y + size) 182 | CGPathAddLineToPoint(path, nil, point.x + size, point.y - size) 183 | CGPathCloseSubpath(path) 184 | 185 | layer.path = path 186 | 187 | } 188 | 189 | func setupSelectionHandleLayers() 190 | { 191 | for var i = 0; i < numberHandles; i++ 192 | { 193 | let layer = CAShapeLayer() 194 | layer.lineWidth = 0.1 195 | layer.strokeColor = NSColor.blueColor().CGColor 196 | layer.fillColor = NSColor.blueColor().CGColor 197 | layer.transform = selectionLayer.transform 198 | 199 | selectionHandleLayers.append(layer) 200 | self.layer!.addSublayer(layer) 201 | } 202 | } 203 | 204 | func removeAnimatedSelection() 205 | { 206 | selectionLayer?.removeFromSuperlayer() 207 | for layer in selectionHandleLayers 208 | { 209 | layer.removeFromSuperlayer() 210 | } 211 | 212 | selectionHandleLayers.removeAll(keepCapacity: true) 213 | selectionLayer = nil 214 | } 215 | 216 | func computeResizedSelectionRectangle(index: Int, dragPoint: NSPoint) -> NSRect 217 | { 218 | var left = Int(selectionRect.origin.x) 219 | var right = Int(selectionRect.origin.x + selectionRect.size.width) 220 | var top = Int(selectionRect.origin.y + selectionRect.size.height) 221 | var bottom = Int(selectionRect.origin.y) 222 | 223 | switch index 224 | { 225 | case 0: 226 | left = Int(dragPoint.x) 227 | case 1: 228 | right = Int(dragPoint.x) 229 | case 2: 230 | top = Int(dragPoint.y) 231 | default: 232 | bottom = Int(dragPoint.y) 233 | } 234 | 235 | return NSRect(x: left, y: bottom, width: right - left, height: top - bottom) 236 | } 237 | 238 | // Coordinates are inverted in this function. (0, 0) is the upper left and y increases down 239 | func trimImage(image: NSImage) 240 | { 241 | let imageRef = image.CGImageForProposedRect(nil, context: nil, hints: nil)?.takeUnretainedValue() 242 | let width = CGImageGetWidth(imageRef) 243 | let height = CGImageGetHeight(imageRef) 244 | let colorSpace = CGColorSpaceCreateDeviceRGB() 245 | 246 | let bytesPerPixel: Int = 4 247 | let bytesPerComponent: Int = 8 248 | let rawData = calloc(height * width * bytesPerPixel, 1) 249 | let pointer = UnsafePointer(rawData) 250 | let bytesPerRow = bytesPerPixel * width 251 | let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue | CGBitmapInfo.ByteOrder32Big.rawValue) 252 | let context = CGBitmapContextCreate(rawData, width, height, bytesPerComponent, bytesPerRow, colorSpace, bitmapInfo.rawValue) 253 | 254 | CGContextDrawImage(context, CGRect(x: 0, y: 0, width: Int(width), height: Int(height)), imageRef) 255 | 256 | var top = 0 257 | var left = 0 258 | var right = Int(width) 259 | var bottom = Int(height) 260 | 261 | for var x = 0; x < Int(width); x++ 262 | { 263 | if scanColumn(x, height: Int(height), width: Int(width), pointer: pointer) 264 | { 265 | left = x 266 | break 267 | } 268 | } 269 | 270 | for var x = Int(width) - 1; x >= 0; x-- 271 | { 272 | if scanColumn(x, height: Int(height), width: Int(width), pointer: pointer) 273 | { 274 | right = x 275 | break 276 | } 277 | } 278 | 279 | for var y = 0; y < Int(height); y++ 280 | { 281 | if scanRow(y, width: Int(width), pointer: pointer) 282 | { 283 | top = y 284 | break 285 | } 286 | } 287 | 288 | for var y = Int(height) - 1; y >= 0; y-- 289 | { 290 | if scanRow(y, width: Int(width), pointer: pointer) 291 | { 292 | bottom = y 293 | break 294 | } 295 | } 296 | 297 | 298 | // Flip the coordinates to be Mac coordinates and add a border around the cropped image 299 | let cropRect = NSRect(x: left - 5, y: Int(height) - bottom - 6, width: right - left + 10, height: bottom - top + 10) 300 | 301 | free(rawData) 302 | 303 | image.lockFocus() 304 | let bitmapRep = NSBitmapImageRep(focusedViewRect: cropRect) 305 | image.unlockFocus() 306 | 307 | let croppedImage = NSImage(data: bitmapRep!.representationUsingType(.NSPNGFileType, properties: [:])!) 308 | cropPoint = cropRect.origin 309 | self.image = croppedImage 310 | } 311 | 312 | func scanRow(y: Int, width:Int, pointer: UnsafePointer) -> Bool 313 | { 314 | for var x = 0; x < width; x++ 315 | { 316 | if pointer[(x + y * width) * 4] != 0xff // only check red, could cause trouble 317 | { 318 | return true 319 | } 320 | } 321 | return false 322 | } 323 | 324 | func scanColumn(x: Int, height: Int, width: Int, pointer: UnsafePointer) -> Bool 325 | { 326 | for var y = 0; y < height; y++ 327 | { 328 | if pointer[(x + y * width) * 4] != 0xff // only check red 329 | { 330 | return true 331 | } 332 | } 333 | return false 334 | } 335 | 336 | 337 | } 338 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Tesseract-Box-Editor/TBE-1-thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enlarsen/Tesseract-Box-Editor/266e89e6bce6811b75eeb4208e23dfe8ce145503/Tesseract-Box-Editor/TBE-1-thumb.jpg -------------------------------------------------------------------------------- /Tesseract-Box-Editor/TBE-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enlarsen/Tesseract-Box-Editor/266e89e6bce6811b75eeb4208e23dfe8ce145503/Tesseract-Box-Editor/TBE-1.jpg -------------------------------------------------------------------------------- /Tesseract-Box-Editor/TBEAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TBEAppDelegate.h 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 5/26/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | #import 29 | 30 | @interface TBEAppDelegate : NSObject 31 | 32 | @property (assign) IBOutlet NSWindow *window; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/TBEAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // TBEAppDelegate.m 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 5/26/14. 6 | // 7 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | #import "TBEAppDelegate.h" 29 | 30 | @implementation TBEAppDelegate 31 | 32 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 33 | { 34 | // Insert code here to initialize your application 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Tesseract Box Editor-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Tesseract-Box-Editor-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | box 13 | 14 | CFBundleTypeName 15 | Tesseract Box File 16 | CFBundleTypeRole 17 | Editor 18 | LSTypeIsPackage 19 | 0 20 | NSDocumentClass 21 | ${PRODUCT_MODULE_NAME}.Document 22 | 23 | 24 | CFBundleExecutable 25 | ${EXECUTABLE_NAME} 26 | CFBundleIconFile 27 | 28 | CFBundleIdentifier 29 | $(PRODUCT_BUNDLE_IDENTIFIER) 30 | CFBundleInfoDictionaryVersion 31 | 6.0 32 | CFBundleName 33 | ${PRODUCT_NAME} 34 | CFBundlePackageType 35 | APPL 36 | CFBundleShortVersionString 37 | 1.0 38 | CFBundleSignature 39 | ???? 40 | CFBundleVersion 41 | 1 42 | LSMinimumSystemVersion 43 | ${MACOSX_DEPLOYMENT_TARGET} 44 | NSHumanReadableCopyright 45 | Copyright © 2014 Erik Larsen. All rights reserved. 46 | NSMainNibFile 47 | MainMenu 48 | NSPrincipalClass 49 | NSApplication 50 | 51 | 52 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/Tesseract-Box-Editor-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1344\cocoasubrtf720 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \vieww9600\viewh8400\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 6 | 7 | \f0\b\fs24 \cf0 \ 8 | Copyright 2014 by Erik Larsen} -------------------------------------------------------------------------------- /Tesseract-Box-Editor/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Tesseract-Box-Editor/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Tesseract-Box-Editor 4 | // 5 | // Created by Erik Larsen on 5/26/14. 6 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) 12 | { 13 | return NSApplicationMain(argc, argv); 14 | } 15 | -------------------------------------------------------------------------------- /Tesseract-Box-EditorTests/Tesseract-Box-EditorTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tesseract-Box-EditorTests/Tesseract_Box_EditorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // Tesseract_Box_EditorTests.m 3 | // Tesseract-Box-EditorTests 4 | // 5 | // Created by Erik Larsen on 5/26/14. 6 | // Copyright (c) 2014 Erik Larsen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Tesseract_Box_EditorTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation Tesseract_Box_EditorTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Tesseract-Box-EditorTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Tesseract Box Editor (TBE) 2 | 3 | An editor for editing .box files created by Tesseract OCR in training mode. 4 | Tesseract is an open source OCR program available at [Tesseract-OCR](https://code.google.com/p/tesseract-ocr/) 5 | TBE was written in Apple's new language, Swift. 6 | 7 | Open a .box file, and the editor will open the corresponding 8 | .tif file (same filename as the box file but with a "tif" extension). 9 | 10 | ## Tips 11 | 12 | Use the up and down arrows to move between quickly between boxes. Type a 13 | character to immediately replace the selected character. Move between pages in multiple 14 | page tiff files with the buttons at the bottom of the screen. 15 | 16 | ## Creating .box files 17 | 18 | Tesseract can be invoked in training mode to create box files: 19 | 20 | ``` 21 | tesseract [lang].[fontname].exp[num].tif [lang].[fontname].exp[num] batch.nochop makebox 22 | ``` 23 | 24 | For example: 25 | 26 | ``` 27 | tesseract eng.swim.exp0.tif eng.swim.exp0 batch.nochop makebox 28 | ``` 29 | 30 | For more information about generating box files see [Training Tesseract](https://code.google.com/p/tesseract-ocr/wiki/TrainingTesseract3) 31 | 32 | ## Downloading 33 | 34 | Downloads are here: [downloads](https://github.com/enlarsen/Tesseract-Box-Editor/releases) 35 | 36 | ## Screenshot 37 | 38 | [![image](https://raw.githubusercontent.com/enlarsen/Tesseract-Box-Editor/master/Tesseract-Box-Editor/TBE-1-thumb.jpg "Screenshot")](https://raw.githubusercontent.com/enlarsen/Tesseract-Box-Editor/master/Tesseract-Box-Editor/TBE-1.jpg) 39 | 40 | ## License 41 | 42 | [MIT License](http://zonorocha.mit-license.org) 43 | --------------------------------------------------------------------------------