├── .gitignore ├── Demo ├── Icon-76.1.png └── Icon-76.png ├── LICENSE ├── README.md ├── StampIcon ├── CGRectExtensions.swift ├── NSColor+Hex.swift ├── Stamper.swift └── main.swift └── stampicon.xcodeproj ├── project.pbxproj └── project.xcworkspace └── contents.xcworkspacedata /.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | TestIcons 3 | -------------------------------------------------------------------------------- /Demo/Icon-76.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorystiefel/stampicon/7aec4a0ad6241ee0c61a6baa49d063bff887a3de/Demo/Icon-76.1.png -------------------------------------------------------------------------------- /Demo/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jorystiefel/stampicon/7aec4a0ad6241ee0c61a6baa49d063bff887a3de/Demo/Icon-76.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 JLS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stampicon 2 | Applies a text label to PNG icons. Stampicon will automatically size the text to fit inside the ribbon, but exceedingly long text on small icons may become unreadable. 3 | 4 | This tool differs from other similar tools in that it renders the icon using Core Graphics, rather than requiring a dependency such as ImageMagick. 5 | 6 | ![Before](https://github.com/jorystiefel/stampicon/blob/master/Demo/Icon-76.png) 7 | ![After](https://github.com/jorystiefel/stampicon/blob/master/Demo/Icon-76.1.png) 8 | 9 | ### Usage 10 | 11 | `stampicon inputfile.png --output outputfile.png --text "text to overlay"` 12 | 13 | option | meaning 14 | --------------|-------------------- 15 | --text | the text to overlay, required 16 | --output | the file to write, required 17 | --font | font family, e.g, defaults to "Helvetica" 18 | --textcolor | the text hex color in hex #FFFFFF, defaults to white 19 | --badgecolor | the hex color of the ribbon behind the text, defaults to red 20 | 21 | ### Thanks 22 | 23 | This utility employs [SwiftCGRectExtensions](https://github.com/nschum/SwiftCGRectExtensions) by @nschum and [UIColor-Hex-Swift](https://github.com/yeahdongcn/UIColor-Hex-Swift/blob/master/UIColorExtension.swift) by @yeahdongcn 24 | -------------------------------------------------------------------------------- /StampIcon/CGRectExtensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 Nikolaj Schumacher 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | // this software and associated documentation files (the "Software"), to deal in 5 | // the Software without restriction, including without limitation the rights to 6 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | // the Software, and to permit persons to whom the Software is furnished to do so, 8 | // subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | import Foundation 21 | 22 | // MARK: CGPoint 23 | 24 | extension CGPoint { 25 | 26 | /// Creates a point with unnamed arguments. 27 | public init(_ x: CGFloat, _ y: CGFloat) { 28 | self.x = x 29 | self.y = y 30 | } 31 | 32 | /// Returns a copy with the x value changed. 33 | public func with(x x: CGFloat) -> CGPoint { 34 | return CGPoint(x: x, y: y) 35 | } 36 | /// Returns a copy with the y value changed. 37 | public func with(y y: CGFloat) -> CGPoint { 38 | return CGPoint(x: x, y: y) 39 | } 40 | } 41 | 42 | // MARK: CGSize 43 | 44 | extension CGSize { 45 | 46 | /// Creates a size with unnamed arguments. 47 | public init(_ width: CGFloat, _ height: CGFloat) { 48 | self.width = width 49 | self.height = height 50 | } 51 | 52 | /// Returns a copy with the width value changed. 53 | public func with(width width: CGFloat) -> CGSize { 54 | return CGSize(width: width, height: height) 55 | } 56 | /// Returns a copy with the height value changed. 57 | public func with(height height: CGFloat) -> CGSize { 58 | return CGSize(width: width, height: height) 59 | } 60 | } 61 | 62 | // MARK: CGRect 63 | 64 | extension CGRect { 65 | 66 | /// Creates a rect with unnamed arguments. 67 | public init(_ origin: CGPoint, _ size: CGSize) { 68 | self.origin = origin 69 | self.size = size 70 | } 71 | 72 | /// Creates a rect with unnamed arguments. 73 | public init(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) { 74 | self.origin = CGPoint(x: x, y: y) 75 | self.size = CGSize(width: width, height: height) 76 | } 77 | 78 | // MARK: access shortcuts 79 | 80 | /// Alias for origin.x. 81 | public var x: CGFloat { 82 | get {return origin.x} 83 | set {origin.x = newValue} 84 | } 85 | /// Alias for origin.y. 86 | public var y: CGFloat { 87 | get {return origin.y} 88 | set {origin.y = newValue} 89 | } 90 | 91 | /// Accesses origin.x + 0.5 * size.width. 92 | public var centerX: CGFloat { 93 | get {return x + width * 0.5} 94 | set {x = newValue - width * 0.5} 95 | } 96 | /// Accesses origin.y + 0.5 * size.height. 97 | public var centerY: CGFloat { 98 | get {return y + height * 0.5} 99 | set {y = newValue - height * 0.5} 100 | } 101 | 102 | // MARK: edges 103 | 104 | /// Alias for origin.x. 105 | public var left: CGFloat { 106 | get {return origin.x} 107 | set {origin.x = newValue} 108 | } 109 | /// Accesses origin.x + size.width. 110 | public var right: CGFloat { 111 | get {return x + width} 112 | set {x = newValue - width} 113 | } 114 | 115 | #if os(iOS) 116 | /// Alias for origin.y. 117 | public var top: CGFloat { 118 | get {return y} 119 | set {y = newValue} 120 | } 121 | /// Accesses origin.y + size.height. 122 | public var bottom: CGFloat { 123 | get {return y + height} 124 | set {y = newValue - height} 125 | } 126 | #else 127 | /// Accesses origin.y + size.height. 128 | public var top: CGFloat { 129 | get {return y + height} 130 | set {y = newValue - height} 131 | } 132 | /// Alias for origin.y. 133 | public var bottom: CGFloat { 134 | get {return y} 135 | set {y = newValue} 136 | } 137 | #endif 138 | 139 | // MARK: points 140 | 141 | /// Accesses the point at the top left corner. 142 | public var topLeft: CGPoint { 143 | get {return CGPoint(x: left, y: top)} 144 | set {left = newValue.x; top = newValue.y} 145 | } 146 | /// Accesses the point at the middle of the top edge. 147 | public var topCenter: CGPoint { 148 | get {return CGPoint(x: centerX, y: top)} 149 | set {centerX = newValue.x; top = newValue.y} 150 | } 151 | /// Accesses the point at the top right corner. 152 | public var topRight: CGPoint { 153 | get {return CGPoint(x: right, y: top)} 154 | set {right = newValue.x; top = newValue.y} 155 | } 156 | 157 | /// Accesses the point at the middle of the left edge. 158 | public var centerLeft: CGPoint { 159 | get {return CGPoint(x: left, y: centerY)} 160 | set {left = newValue.x; centerY = newValue.y} 161 | } 162 | /// Accesses the point at the center. 163 | public var center: CGPoint { 164 | get {return CGPoint(x: centerX, y: centerY)} 165 | set {centerX = newValue.x; centerY = newValue.y} 166 | } 167 | /// Accesses the point at the middle of the right edge. 168 | public var centerRight: CGPoint { 169 | get {return CGPoint(x: right, y: centerY)} 170 | set {right = newValue.x; centerY = newValue.y} 171 | } 172 | 173 | /// Accesses the point at the bottom left corner. 174 | public var bottomLeft: CGPoint { 175 | get {return CGPoint(x: left, y: bottom)} 176 | set {left = newValue.x; bottom = newValue.y} 177 | } 178 | /// Accesses the point at the middle of the bottom edge. 179 | public var bottomCenter: CGPoint { 180 | get {return CGPoint(x: centerX, y: bottom)} 181 | set {centerX = newValue.x; bottom = newValue.y} 182 | } 183 | /// Accesses the point at the bottom right corner. 184 | public var bottomRight: CGPoint { 185 | get {return CGPoint(x: right, y: bottom)} 186 | set {right = newValue.x; bottom = newValue.y} 187 | } 188 | 189 | // MARK: with 190 | 191 | /// Returns a copy with the origin value changed. 192 | public func with(origin origin: CGPoint) -> CGRect { 193 | return CGRect(origin: origin, size: size) 194 | } 195 | /// Returns a copy with the x and y values changed. 196 | public func with(x x: CGFloat, y: CGFloat) -> CGRect { 197 | return with(origin: CGPoint(x: x, y: y)) 198 | } 199 | /// Returns a copy with the x value changed. 200 | public func with(x x: CGFloat) -> CGRect { 201 | return with(x: x, y: y) 202 | } 203 | /// Returns a copy with the y value changed. 204 | public func with(y y: CGFloat) -> CGRect { 205 | return with(x: x, y: y) 206 | } 207 | 208 | /// Returns a copy with the size value changed. 209 | public func with(size size: CGSize) -> CGRect { 210 | return CGRect(origin: origin, size: size) 211 | } 212 | /// Returns a copy with the width and height values changed. 213 | public func with(width width: CGFloat, height: CGFloat) -> CGRect { 214 | return with(size: CGSize(width: width, height: height)) 215 | } 216 | /// Returns a copy with the width value changed. 217 | public func with(width width: CGFloat) -> CGRect { 218 | return with(width: width, height: height) 219 | } 220 | /// Returns a copy with the height value changed. 221 | public func with(height height: CGFloat) -> CGRect { 222 | return with(width: width, height: height) 223 | } 224 | 225 | /// Returns a copy with the x and width values changed. 226 | public func with(x x: CGFloat, width: CGFloat) -> CGRect { 227 | return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height)) 228 | } 229 | /// Returns a copy with the y and height values changed. 230 | public func with(y y: CGFloat, height: CGFloat) -> CGRect { 231 | return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height)) 232 | } 233 | 234 | // MARK: offset 235 | 236 | /// Returns a copy with the x and y values offset. 237 | public func offsetBy(dx: CGFloat, _ dy: CGFloat) -> CGRect { 238 | return with(x: x + dx, y: y + dy) 239 | } 240 | /// Returns a copy with the x value values offset. 241 | public func offsetBy(dx dx: CGFloat) -> CGRect { 242 | return with(x: x + dx) 243 | } 244 | /// Returns a copy with the y value values offset. 245 | public func offsetBy(dy dy: CGFloat) -> CGRect { 246 | return with(y: y + dy) 247 | } 248 | /// Returns a copy with the x and y values offset. 249 | public func offsetBy(by: CGSize) -> CGRect { 250 | return with(x: x + by.width, y: y + by.height) 251 | } 252 | 253 | /// Modifies the x and y values by offsetting. 254 | public mutating func offsetInPlace(dx: CGFloat, _ dy: CGFloat) { 255 | offsetInPlace(dx: dx, dy: dy) 256 | } 257 | /// Modifies the x value values by offsetting. 258 | public mutating func offsetInPlace(dx dx: CGFloat = 0) { 259 | x += dx 260 | } 261 | /// Modifies the y value values by offsetting. 262 | public mutating func offsetInPlace(dy dy: CGFloat = 0) { 263 | y += dy 264 | } 265 | /// Modifies the x and y values by offsetting. 266 | public mutating func offsetInPlace(by: CGSize) { 267 | offsetInPlace(dx: by.width, dy: by.height) 268 | } 269 | 270 | // MARK: inset 271 | 272 | /// Returns a copy inset on all edges by the same value. 273 | public func insetBy(by: CGFloat) -> CGRect { 274 | return insetBy(dx: by, dy: by) 275 | } 276 | 277 | /// Returns a copy inset on the left and right edges. 278 | public func insetBy(dx dx: CGFloat) -> CGRect { 279 | return with(x: x + dx, width: width - dx * 2) 280 | } 281 | /// Returns a copy inset on the top and bottom edges. 282 | public func insetBy(dy dy: CGFloat) -> CGRect { 283 | return with(y: y + dy, height: height - dy * 2) 284 | } 285 | 286 | /// Returns a copy inset on all edges by different values. 287 | public func insetBy(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect { 288 | return CGRect(x: x + minX, y: y + minY, width: width - minX - maxX, height: height - minY - maxY) 289 | } 290 | 291 | /// Returns a copy inset on all edges by different values. 292 | public func insetBy(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect { 293 | #if os(iOS) 294 | return CGRect(x: x + left, y: y + top, width: width - right - left, height: height - top - bottom) 295 | #else 296 | return CGRect(x: x + left, y: y + bottom, width: width - right - left, height: height - top - bottom) 297 | #endif 298 | } 299 | 300 | /// Returns a copy inset on the top and left edges. 301 | public func insetBy(topLeft topLeft: CGSize) -> CGRect { 302 | return insetBy(top: topLeft.height, left: topLeft.width) 303 | } 304 | /// Returns a copy inset on the top and right edges. 305 | public func insetBy(topRight topRight: CGSize) -> CGRect { 306 | return insetBy(top: topRight.height, right: topRight.width) 307 | } 308 | /// Returns a copy inset on the bottom and left edges. 309 | public func insetBy(bottomLeft bottomLeft: CGSize) -> CGRect { 310 | return insetBy(bottom: bottomLeft.height, left: bottomLeft.width) 311 | } 312 | /// Returns a copy inset on the bottom and right edges. 313 | public func insetBy(bottomRight bottomRight: CGSize) -> CGRect { 314 | return insetBy(bottom: bottomRight.height, right: bottomRight.width) 315 | } 316 | 317 | /// Modifies all values by insetting all edges by the same value. 318 | public mutating func insetInPlace(by: CGFloat) { 319 | insetInPlace(dx: by, dy: by) 320 | } 321 | 322 | /// Modifies all values by insetting on the left and right edges. 323 | public mutating func insetInPlace(dx dx: CGFloat) { 324 | insetInPlace(dx: dx, dy: 0) 325 | } 326 | /// Modifies all values by insetting on the top and bottom edges. 327 | public mutating func insetInPlace(dy dy: CGFloat) { 328 | insetInPlace(dx: 0, dy: dy) 329 | } 330 | 331 | /// Modifies all values by insetting all edges by different value. 332 | public mutating func insetInPlace(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) { 333 | self = insetBy(minX: minX, minY: minY, maxX: maxX, maxY: maxY) 334 | } 335 | 336 | /// Modifies all values by insetting all edges by different value. 337 | public mutating func insetInPlace(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) { 338 | self = insetBy(top: top, left: left, bottom: bottom, right: right) 339 | } 340 | 341 | /// Modifies all values by insetting the top and left edges. 342 | public mutating func insetInPlace(topLeft topLeft: CGSize) { 343 | self = insetBy(topLeft: topLeft) 344 | } 345 | /// Modifies all values by insetting the top and right edges. 346 | public mutating func insetInPlace(topRight topRight: CGSize) { 347 | self = insetBy(topRight: topRight) 348 | } 349 | /// Modifies all values by insetting the bottom and left edges. 350 | public mutating func insetInPlace(bottomLeft bottomLeft: CGSize) { 351 | self = insetBy(bottomLeft: bottomLeft) 352 | } 353 | /// Modifies all values by insetting the bottom and right edges. 354 | public mutating func insetInPlace(bottomRight bottomRight: CGSize) { 355 | self = insetBy(bottomRight: bottomRight) 356 | } 357 | 358 | // MARK: extending 359 | 360 | /// Returns a copy extended on all edges by different values. 361 | public func extendBy(dx dx: CGFloat, dy: CGFloat = 0) -> CGRect { 362 | return insetBy(dx: -dx, dy: -dy) 363 | } 364 | /// Returns a copy extended on the top and bottom edges. 365 | public func extendBy(dy dy: CGFloat) -> CGRect { 366 | return insetBy(dy: -dy) 367 | } 368 | 369 | /// Returns a copy extended on all edges by the same value. 370 | public func extendBy(by: CGFloat) -> CGRect { 371 | return insetBy(dx: -by, dy: -by) 372 | } 373 | 374 | /// Returns a copy extended on all edges by different values. 375 | public func extendBy(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect { 376 | return insetBy(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY) 377 | } 378 | /// Returns a copy extended on all edges by different values. 379 | public func extendBy(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect { 380 | return insetBy(top: -top, left: -left, bottom: -bottom, right: -right) 381 | } 382 | 383 | /// Modifies all values by extending the top and left edges. 384 | public func extendBy(topLeft topLeft: CGSize) -> CGRect { 385 | return extendBy(top: topLeft.height, left: topLeft.width) 386 | } 387 | /// Modifies all values by extending the top and right edges. 388 | public func extendBy(topRight topRight: CGSize) -> CGRect { 389 | return insetBy(top: -topRight.height, right: -topRight.width) 390 | } 391 | /// Modifies all values by extending the bottom and left edges. 392 | public func extendBy(bottomLeft bottomLeft: CGSize) -> CGRect { 393 | return insetBy(bottom: -bottomLeft.height, left: -bottomLeft.width) 394 | } 395 | /// Modifies all values by extending the bottom and right edges. 396 | public func extendBy(bottomRight bottomRight: CGSize) -> CGRect { 397 | return insetBy(bottom: -bottomRight.height, right: -bottomRight.width) 398 | } 399 | 400 | /// Modifies all values by extending all edges by different values. 401 | public mutating func extendInPlace(dx dx: CGFloat, dy: CGFloat = 0) { 402 | self = insetBy(dx: -dx, dy: -dy) 403 | } 404 | /// Modifies all values by extending the top and bottom edges. 405 | public mutating func extendInPlace(dy dy: CGFloat) { 406 | self = insetBy(dy: -dy) 407 | } 408 | 409 | /// Modifies all values by extending all edges by the same value. 410 | public mutating func extendInPlace(by: CGFloat) { 411 | self = insetBy(dx: -by, dy: -by) 412 | } 413 | 414 | /// Modifies all values by extending all edges by different values. 415 | public mutating func extendInPlace(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) { 416 | self = insetBy(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY) 417 | } 418 | /// Modifies all values by extending all edges by different values. 419 | public mutating func extendInPlace(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) { 420 | self = insetBy(top: -top, left: -left, bottom: -bottom, right: -right) 421 | } 422 | 423 | /// Modifies all values by extending the top and left edges. 424 | public mutating func extendInPlace(topLeft topLeft: CGSize) { 425 | self = extendBy(top: topLeft.height, left: topLeft.width) 426 | } 427 | /// Modifies all values by extending the top and right edges. 428 | public mutating func extendInPlace(topRight topRight: CGSize) { 429 | self = insetBy(top: -topRight.height, right: -topRight.width) 430 | } 431 | /// Modifies all values by extending the bottom and left edges. 432 | public mutating func extendInPlace(bottomLeft bottomLeft: CGSize) { 433 | self = insetBy(bottom: -bottomLeft.height, left: -bottomLeft.width) 434 | } 435 | /// Modifies all values by extending the bottom and right edges. 436 | public mutating func extendInPlace(bottomRight bottomRight: CGSize) { 437 | self = insetBy(bottom: -bottomRight.height, right: -bottomRight.width) 438 | } 439 | 440 | // MARK: sizes 441 | 442 | /// Returns a rect of the specified size centered in this rect. 443 | public func center(size: CGSize) -> CGRect { 444 | let dx = width - size.width 445 | let dy = height - size.height 446 | return CGRect(x: x + dx * 0.5, y: y + dy * 0.5, width: size.width, height: size.height) 447 | } 448 | 449 | /// Returns a rect of the specified size centered in this rect touching the specified edge. 450 | public func center(size: CGSize, alignTo edge: CGRectEdge) -> CGRect { 451 | return CGRect(origin: alignedOrigin(size, edge: edge), size: size) 452 | } 453 | 454 | private func alignedOrigin(size: CGSize, edge: CGRectEdge) -> CGPoint { 455 | let dx = width - size.width 456 | let dy = height - size.height 457 | switch edge { 458 | case .MinXEdge: 459 | return CGPoint(x: x, y: y + dy * 0.5) 460 | case .MinYEdge: 461 | return CGPoint(x: x + dx * 0.5, y: y) 462 | case .MaxXEdge: 463 | return CGPoint(x: x + dx, y: y + dy * 0.5) 464 | case .MaxYEdge: 465 | return CGPoint(x: x + dx * 0.5, y: y + dy) 466 | } 467 | } 468 | 469 | /// Returns a rect of the specified size centered in this rect touching the specified corner. 470 | public func align(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGRect { 471 | return CGRect(origin: alignedOrigin(size, corner: e1, e2), size: size) 472 | } 473 | 474 | private func alignedOrigin(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGPoint { 475 | let dx = width - size.width 476 | let dy = height - size.height 477 | switch (e1, e2) { 478 | case (.MinXEdge, .MinYEdge), (.MinYEdge, .MinXEdge): 479 | return CGPoint(x: x, y: y) 480 | case (.MaxXEdge, .MinYEdge), (.MinYEdge, .MaxXEdge): 481 | return CGPoint(x: x + dx, y: y) 482 | case (.MinXEdge, .MaxYEdge), (.MaxYEdge, .MinXEdge): 483 | return CGPoint(x: x, y: y + dy) 484 | case (.MaxXEdge, .MaxYEdge), (.MaxYEdge, .MaxXEdge): 485 | return CGPoint(x: x + dx, y: y + dy) 486 | default: 487 | preconditionFailure("Cannot align to this combination of edges") 488 | } 489 | } 490 | 491 | /// Modifies all values by setting the size while centering the rect. 492 | public mutating func centerInPlace(size: CGSize) { 493 | self = center(size) 494 | } 495 | 496 | /// Modifies all values by setting the size while centering the rect touching the specified edge. 497 | public mutating func centerInPlace(size: CGSize, alignTo edge: CGRectEdge) { 498 | self = center(size, alignTo: edge) 499 | } 500 | 501 | /// Modifies all values by setting the size while centering the rect touching the specified corner. 502 | public mutating func alignInPlace(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) { 503 | self = align(size, corner: e1, e2) 504 | } 505 | } 506 | 507 | // MARK: transform 508 | 509 | extension CGAffineTransform: Equatable { 510 | } 511 | 512 | public func ==(t1: CGAffineTransform, t2: CGAffineTransform) -> Bool { 513 | return CGAffineTransformEqualToTransform(t1, t2) 514 | } 515 | 516 | extension CGAffineTransform: CustomDebugStringConvertible { 517 | public var debugDescription: String { 518 | return "(\(a),\(b),\(c),\(d),\(tx),\(ty))" 519 | } 520 | } 521 | 522 | // MARK: operators 523 | 524 | /// Returns a point by adding the coordinates of another point. 525 | public func +(p1: CGPoint, p2: CGPoint) -> CGPoint { 526 | return CGPoint(x: p1.x + p2.x, y: p1.y + p2.y) 527 | } 528 | /// Modifies the x and y values by adding the coordinates of another point. 529 | public func +=(inout p1: CGPoint, p2: CGPoint) { 530 | p1.x += p2.x 531 | p1.y += p2.y 532 | } 533 | /// Returns a point by subtracting the coordinates of another point. 534 | public func -(p1: CGPoint, p2: CGPoint) -> CGPoint { 535 | return CGPoint(x: p1.x - p2.x, y: p1.y - p2.y) 536 | } 537 | /// Modifies the x and y values by subtracting the coordinates of another points. 538 | public func -=(inout p1: CGPoint, p2: CGPoint) { 539 | p1.x -= p2.x 540 | p1.y -= p2.y 541 | } 542 | 543 | /// Returns a point by adding a size to the coordinates. 544 | public func +(point: CGPoint, size: CGSize) -> CGPoint { 545 | return CGPoint(x: point.x + size.width, y: point.y + size.height) 546 | } 547 | /// Modifies the x and y values by adding a size to the coordinates. 548 | public func +=(inout point: CGPoint, size: CGSize) { 549 | point.x += size.width 550 | point.y += size.height 551 | } 552 | /// Returns a point by subtracting a size from the coordinates. 553 | public func -(point: CGPoint, size: CGSize) -> CGPoint { 554 | return CGPoint(x: point.x - size.width, y: point.y - size.height) 555 | } 556 | /// Modifies the x and y values by subtracting a size from the coordinates. 557 | public func -=(inout point: CGPoint, size: CGSize) { 558 | point.x -= size.width 559 | point.y -= size.height 560 | } 561 | 562 | /// Returns a point by adding a tuple to the coordinates. 563 | public func +(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { 564 | return CGPoint(x: point.x + tuple.0, y: point.y + tuple.1) 565 | } 566 | /// Modifies the x and y values by adding a tuple to the coordinates. 567 | public func +=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { 568 | point.x += tuple.0 569 | point.y += tuple.1 570 | } 571 | /// Returns a point by subtracting a tuple from the coordinates. 572 | public func -(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { 573 | return CGPoint(x: point.x - tuple.0, y: point.y - tuple.1) 574 | } 575 | /// Modifies the x and y values by subtracting a tuple from the coordinates. 576 | public func -=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { 577 | point.x -= tuple.0 578 | point.y -= tuple.1 579 | } 580 | /// Returns a point by multiplying the coordinates with a value. 581 | public func *(point: CGPoint, factor: CGFloat) -> CGPoint { 582 | return CGPoint(x: point.x * factor, y: point.y * factor) 583 | } 584 | /// Modifies the x and y values by multiplying the coordinates with a value. 585 | public func *=(inout point: CGPoint, factor: CGFloat) { 586 | point.x *= factor 587 | point.y *= factor 588 | } 589 | /// Returns a point by multiplying the coordinates with a tuple. 590 | public func *(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { 591 | return CGPoint(x: point.x * tuple.0, y: point.y * tuple.1) 592 | } 593 | /// Modifies the x and y values by multiplying the coordinates with a tuple. 594 | public func *=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { 595 | point.x *= tuple.0 596 | point.y *= tuple.1 597 | } 598 | /// Returns a point by dividing the coordinates by a tuple. 599 | public func /(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { 600 | return CGPoint(x: point.x / tuple.0, y: point.y / tuple.1) 601 | } 602 | /// Modifies the x and y values by dividing the coordinates by a tuple. 603 | public func /=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { 604 | point.x /= tuple.0 605 | point.y /= tuple.1 606 | } 607 | /// Returns a point by dividing the coordinates by a factor. 608 | public func /(point: CGPoint, factor: CGFloat) -> CGPoint { 609 | return CGPoint(x: point.x / factor, y: point.y / factor) 610 | } 611 | /// Modifies the x and y values by dividing the coordinates by a factor. 612 | public func /=(inout point: CGPoint, factor: CGFloat) { 613 | point.x /= factor 614 | point.y /= factor 615 | } 616 | 617 | /// Returns a point by adding another size. 618 | public func +(s1: CGSize, s2: CGSize) -> CGSize { 619 | return CGSize(width: s1.width + s2.width, height: s1.height + s2.height) 620 | } 621 | /// Modifies the width and height values by adding another size. 622 | public func +=(inout s1: CGSize, s2: CGSize) { 623 | s1.width += s2.width 624 | s1.height += s2.height 625 | } 626 | /// Returns a point by subtracting another size. 627 | public func -(s1: CGSize, s2: CGSize) -> CGSize { 628 | return CGSize(width: s1.width - s2.width, height: s1.height - s2.height) 629 | } 630 | /// Modifies the width and height values by subtracting another size. 631 | public func -=(inout s1: CGSize, s2: CGSize) { 632 | s1.width -= s2.width 633 | s1.height -= s2.height 634 | } 635 | 636 | /// Returns a point by adding a tuple. 637 | public func +(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { 638 | return CGSize(width: size.width + tuple.0, height: size.height + tuple.1) 639 | } 640 | /// Modifies the width and height values by adding a tuple. 641 | public func +=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { 642 | size.width += tuple.0 643 | size.height += tuple.1 644 | } 645 | /// Returns a point by subtracting a tuple. 646 | public func -(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { 647 | return CGSize(width: size.width - tuple.0, height: size.height - tuple.1) 648 | } 649 | /// Modifies the width and height values by subtracting a tuple. 650 | public func -=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { 651 | size.width -= tuple.0 652 | size.height -= tuple.1 653 | } 654 | /// Returns a point by multiplying the size with a factor. 655 | public func *(size: CGSize, factor: CGFloat) -> CGSize { 656 | return CGSize(width: size.width * factor, height: size.height * factor) 657 | } 658 | /// Modifies the width and height values by multiplying them with a factor. 659 | public func *=(inout size: CGSize, factor: CGFloat) { 660 | size.width *= factor 661 | size.height *= factor 662 | } 663 | /// Returns a point by multiplying the size with a tuple. 664 | public func *(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { 665 | return CGSize(width: size.width * tuple.0, height: size.height * tuple.1) 666 | } 667 | /// Modifies the width and height values by multiplying them with a tuple. 668 | public func *=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { 669 | size.width *= tuple.0 670 | size.height *= tuple.1 671 | } 672 | /// Returns a point by dividing the size by a factor. 673 | public func /(size: CGSize, factor: CGFloat) -> CGSize { 674 | return CGSize(width: size.width / factor, height: size.height / factor) 675 | } 676 | /// Modifies the width and height values by dividing them by a factor. 677 | public func /=(inout size: CGSize, factor: CGFloat) { 678 | size.width /= factor 679 | size.height /= factor 680 | } 681 | /// Returns a point by dividing the size by a tuple. 682 | public func /(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { 683 | return CGSize(width: size.width / tuple.0, height: size.height / tuple.1) 684 | } 685 | /// Modifies the width and height values by dividing them by a tuple. 686 | public func /=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { 687 | size.width /= tuple.0 688 | size.height /= tuple.1 689 | } 690 | 691 | /// Returns a rect by adding the coordinates of a point to the origin. 692 | public func +(rect: CGRect, point: CGPoint) -> CGRect { 693 | return CGRect(origin: rect.origin + point, size: rect.size) 694 | } 695 | /// Modifies the x and y values by adding the coordinates of a point. 696 | public func +=(inout rect: CGRect, point: CGPoint) { 697 | rect.origin += point 698 | } 699 | /// Returns a rect by subtracting the coordinates of a point from the origin. 700 | public func -(rect: CGRect, point: CGPoint) -> CGRect { 701 | return CGRect(origin: rect.origin - point, size: rect.size) 702 | } 703 | /// Modifies the x and y values by subtracting the coordinates from a point. 704 | public func -=(inout rect: CGRect, point: CGPoint) { 705 | rect.origin -= point 706 | } 707 | 708 | /// Returns a rect by adding a size to the size. 709 | public func +(rect: CGRect, size: CGSize) -> CGRect { 710 | return CGRect(origin: rect.origin, size: rect.size + size) 711 | } 712 | /// Modifies the width and height values by adding a size. 713 | public func +=(inout rect: CGRect, size: CGSize) { 714 | rect.size += size 715 | } 716 | /// Returns a rect by subtracting a size from the size. 717 | public func -(rect: CGRect, size: CGSize) -> CGRect { 718 | return CGRect(origin: rect.origin, size: rect.size - size) 719 | } 720 | /// Modifies the width and height values by subtracting a size. 721 | public func -=(inout rect: CGRect, size: CGSize) { 722 | rect.size -= size 723 | } 724 | 725 | /// Returns a point by applying a transform. 726 | public func *(point: CGPoint, transform: CGAffineTransform) -> CGPoint { 727 | return CGPointApplyAffineTransform(point, transform) 728 | } 729 | /// Modifies all values by applying a transform. 730 | public func *=(inout point: CGPoint, transform: CGAffineTransform) { 731 | point = CGPointApplyAffineTransform(point, transform) 732 | } 733 | /// Returns a size by applying a transform. 734 | public func *(size: CGSize, transform: CGAffineTransform) -> CGSize { 735 | return CGSizeApplyAffineTransform(size, transform) 736 | } 737 | /// Modifies all values by applying a transform. 738 | public func *=(inout size: CGSize, transform: CGAffineTransform) { 739 | size = CGSizeApplyAffineTransform(size, transform) 740 | } 741 | /// Returns a rect by applying a transform. 742 | public func *(rect: CGRect, transform: CGAffineTransform) -> CGRect { 743 | return CGRectApplyAffineTransform(rect, transform) 744 | } 745 | /// Modifies all values by applying a transform. 746 | public func *=(inout rect: CGRect, transform: CGAffineTransform) { 747 | rect = CGRectApplyAffineTransform(rect, transform) 748 | } 749 | 750 | /// Returns a transform by concatenating two transforms. 751 | public func *(t1: CGAffineTransform, t2: CGAffineTransform) -> CGAffineTransform { 752 | return CGAffineTransformConcat(t1, t2) 753 | } 754 | /// Modifies all values by concatenating another transform. 755 | public func *=(inout t1: CGAffineTransform, t2: CGAffineTransform) { 756 | t1 = CGAffineTransformConcat(t1, t2) 757 | } -------------------------------------------------------------------------------- /StampIcon/NSColor+Hex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColorExtension.swift 3 | // UIColor-Hex-Swift 4 | // 5 | // Created by R0CKSTAR on 6/13/14. 6 | // Copyright (c) 2014 P.D.Q. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSColor { 12 | public convenience init(rgba: String) { 13 | var red: CGFloat = 0.0 14 | var green: CGFloat = 0.0 15 | var blue: CGFloat = 0.0 16 | var alpha: CGFloat = 1.0 17 | 18 | if rgba.hasPrefix("#") { 19 | let index = rgba.startIndex.advancedBy(1) 20 | let hex = rgba.substringFromIndex(index) 21 | let scanner = NSScanner(string: hex) 22 | var hexValue: CUnsignedLongLong = 0 23 | if scanner.scanHexLongLong(&hexValue) { 24 | switch (hex.characters.count) { 25 | case 3: 26 | red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 27 | green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 28 | blue = CGFloat(hexValue & 0x00F) / 15.0 29 | case 4: 30 | red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 31 | green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 32 | blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 33 | alpha = CGFloat(hexValue & 0x000F) / 15.0 34 | case 6: 35 | red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 36 | green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 37 | blue = CGFloat(hexValue & 0x0000FF) / 255.0 38 | case 8: 39 | red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 40 | green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 41 | blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 42 | alpha = CGFloat(hexValue & 0x000000FF) / 255.0 43 | default: 44 | print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8") 45 | } 46 | } else { 47 | print("Scan hex error") 48 | } 49 | } else { 50 | print("Invalid RGB string, missing '#' as prefix") 51 | } 52 | self.init(red:red, green:green, blue:blue, alpha:alpha) 53 | } 54 | } -------------------------------------------------------------------------------- /StampIcon/Stamper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BadgeImage.swift 3 | // StampIcon 4 | // 5 | // Created by Jory Stiefel on 9/12/15. 6 | // Copyright © 2015 Jory Stiefel. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | let kVerticalPadding: CGFloat = 3.0 12 | let kHorizontalPadding: CGFloat = 7.0 13 | let kMaxFontSize: CGFloat = 22.0 14 | let π = CGFloat(M_PI) 15 | 16 | struct StampConfig { 17 | var inputFile = "" 18 | var outputFile = "" 19 | var text = "" 20 | var textColor = NSColor.whiteColor() 21 | var badgeColor = NSColor(red: 0.98, green: 0.13, blue: 0.15, alpha: 1.0) 22 | var fontName = "Helvetica" 23 | } 24 | 25 | struct Stamper { 26 | 27 | let config: StampConfig 28 | 29 | func generateBadgeImage(maxWidth: CGFloat, maxHeight: CGFloat) -> NSImage { 30 | 31 | var textFontSize = kMaxFontSize 32 | var textFontAttributes = [ 33 | NSFontAttributeName: NSFont(name: config.fontName, size: textFontSize)!, 34 | NSForegroundColorAttributeName: config.textColor 35 | ] 36 | 37 | let drawText = config.text 38 | 39 | let badgeImage = NSImage(size: NSSize(width:maxWidth, height:maxHeight), flipped: false) { rect -> Bool in 40 | 41 | let context = NSGraphicsContext.currentContext()?.CGContext 42 | 43 | var textSize = drawText.sizeWithAttributes(textFontAttributes) 44 | while (textSize.width > maxWidth * 0.7) { 45 | textFontSize -= 0.25 46 | textFontAttributes[NSFontAttributeName] = NSFont(name: self.config.fontName, size: textFontSize)! 47 | textSize = drawText.sizeWithAttributes(textFontAttributes) 48 | } 49 | 50 | var badgeRect = rect.center(CGSize(width: maxWidth * 2, height: textSize.height + kVerticalPadding)) 51 | 52 | badgeRect.origin.x += (1.0/6) * maxWidth 53 | badgeRect.origin.y -= (1.0/6) * maxHeight 54 | let textRect = badgeRect.center(textSize) 55 | 56 | CGContextTranslateCTM(context, badgeRect.center.x, badgeRect.center.y) 57 | CGContextRotateCTM(context, π / 4) 58 | CGContextTranslateCTM(context, -badgeRect.center.x, -badgeRect.center.y) 59 | 60 | self.config.badgeColor.setFill() 61 | NSRectFill(badgeRect) 62 | 63 | drawText.drawInRect(textRect, withAttributes: textFontAttributes) 64 | 65 | return true 66 | } 67 | 68 | return badgeImage 69 | } 70 | 71 | func generateOutputImage() -> NSImage? { 72 | guard let inputImage = NSImage(contentsOfFile: self.config.inputFile) else { 73 | print("Could not read input file") 74 | exit(1) 75 | } 76 | 77 | let badgeImage = stamper.generateBadgeImage(inputImage.size.width, maxHeight: inputImage.size.height) 78 | 79 | let outputImage = NSImage(size: inputImage.size, flipped: false, drawingHandler: { (rect) -> Bool in 80 | inputImage.drawInRect(rect) 81 | badgeImage.drawInRect(rect) 82 | return true 83 | }) 84 | 85 | return outputImage 86 | } 87 | 88 | func writeImageFile(image: NSImage, filename: String) { 89 | 90 | if let imageData = image.TIFFRepresentation, 91 | let pngRepresentation: NSData = NSBitmapImageRep(data: imageData)?.representationUsingType(.NSPNGFileType, properties: [:]) 92 | { 93 | let success = pngRepresentation.writeToFile(filename, atomically: true) 94 | if !success { 95 | print("Error writing file") 96 | exit(1) 97 | } 98 | } 99 | } 100 | 101 | func processStamp() { 102 | 103 | if let outputImage = self.generateOutputImage() { 104 | writeImageFile(outputImage, filename: config.outputFile) 105 | } else { 106 | print("Could not generate output image") 107 | } 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /StampIcon/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // StampIcon 4 | // 5 | // Created by Jory Stiefel on 9/12/15. 6 | // Copyright © 2015 Jory Stiefel. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | func generateConfigFromArguments() -> StampConfig { 12 | 13 | var config = StampConfig() 14 | 15 | guard Process.arguments.count > 1 else { 16 | print("stampicon by Jory Stiefel") 17 | print("Usage: stampicon iconfile.png --text \"stamp text\" --output outputfile.png\n") 18 | exit(0) 19 | } 20 | 21 | for (idx, argument) in Process.arguments.dropFirst().enumerate() { 22 | guard idx > 0 else { 23 | config.inputFile = argument 24 | continue 25 | } 26 | 27 | let argIdx = idx + 1 28 | 29 | switch argument { 30 | case "--text": 31 | config.text = Process.arguments[argIdx+1] 32 | 33 | case "--output": 34 | config.outputFile = Process.arguments[argIdx+1] 35 | 36 | case "--font": 37 | config.fontName = Process.arguments[argIdx+1] 38 | 39 | case "--textcolor": 40 | config.textColor = NSColor(rgba: Process.arguments[argIdx+1]) 41 | 42 | case "--badgecolor": 43 | config.badgeColor = NSColor(rgba: Process.arguments[argIdx+1]) 44 | 45 | default: 46 | continue 47 | } 48 | 49 | } 50 | 51 | return config 52 | } 53 | 54 | var config = generateConfigFromArguments() 55 | let stamper = Stamper(config: config) 56 | stamper.processStamp() 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /stampicon.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C9E141F1BA4790200B6EF0E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E141E1BA4790200B6EF0E /* main.swift */; }; 11 | 0C9E14261BA4828900B6EF0E /* Stamper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E14251BA4828900B6EF0E /* Stamper.swift */; }; 12 | 0C9E14281BA4B73300B6EF0E /* NSColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E14271BA4B73300B6EF0E /* NSColor+Hex.swift */; }; 13 | 0C9E142A1BA4C6A000B6EF0E /* CGRectExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E14291BA4C6A000B6EF0E /* CGRectExtensions.swift */; settings = {ASSET_TAGS = (); }; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | 0C9E14191BA4790200B6EF0E /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = /usr/share/man/man1/; 21 | dstSubfolderSpec = 0; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 1; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 0C9E141B1BA4790200B6EF0E /* stampicon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = stampicon; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 0C9E141E1BA4790200B6EF0E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 31 | 0C9E14251BA4828900B6EF0E /* Stamper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stamper.swift; sourceTree = ""; }; 32 | 0C9E14271BA4B73300B6EF0E /* NSColor+Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSColor+Hex.swift"; sourceTree = ""; }; 33 | 0C9E14291BA4C6A000B6EF0E /* CGRectExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGRectExtensions.swift; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | 0C9E14181BA4790200B6EF0E /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 0C9E14121BA4790200B6EF0E = { 48 | isa = PBXGroup; 49 | children = ( 50 | 0C9E141D1BA4790200B6EF0E /* StampIcon */, 51 | 0C9E141C1BA4790200B6EF0E /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 0C9E141C1BA4790200B6EF0E /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 0C9E141B1BA4790200B6EF0E /* stampicon */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 0C9E141D1BA4790200B6EF0E /* StampIcon */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 0C9E141E1BA4790200B6EF0E /* main.swift */, 67 | 0C9E14251BA4828900B6EF0E /* Stamper.swift */, 68 | 0C9E14291BA4C6A000B6EF0E /* CGRectExtensions.swift */, 69 | 0C9E14271BA4B73300B6EF0E /* NSColor+Hex.swift */, 70 | ); 71 | path = StampIcon; 72 | sourceTree = ""; 73 | }; 74 | /* End PBXGroup section */ 75 | 76 | /* Begin PBXNativeTarget section */ 77 | 0C9E141A1BA4790200B6EF0E /* stampicon */ = { 78 | isa = PBXNativeTarget; 79 | buildConfigurationList = 0C9E14221BA4790200B6EF0E /* Build configuration list for PBXNativeTarget "stampicon" */; 80 | buildPhases = ( 81 | 0C9E14171BA4790200B6EF0E /* Sources */, 82 | 0C9E14181BA4790200B6EF0E /* Frameworks */, 83 | 0C9E14191BA4790200B6EF0E /* CopyFiles */, 84 | ); 85 | buildRules = ( 86 | ); 87 | dependencies = ( 88 | ); 89 | name = stampicon; 90 | productName = StampIcon; 91 | productReference = 0C9E141B1BA4790200B6EF0E /* stampicon */; 92 | productType = "com.apple.product-type.tool"; 93 | }; 94 | /* End PBXNativeTarget section */ 95 | 96 | /* Begin PBXProject section */ 97 | 0C9E14131BA4790200B6EF0E /* Project object */ = { 98 | isa = PBXProject; 99 | attributes = { 100 | LastSwiftUpdateCheck = 0700; 101 | LastUpgradeCheck = 0700; 102 | ORGANIZATIONNAME = "Jory Stiefel"; 103 | TargetAttributes = { 104 | 0C9E141A1BA4790200B6EF0E = { 105 | CreatedOnToolsVersion = 7.0; 106 | }; 107 | }; 108 | }; 109 | buildConfigurationList = 0C9E14161BA4790200B6EF0E /* Build configuration list for PBXProject "stampicon" */; 110 | compatibilityVersion = "Xcode 3.2"; 111 | developmentRegion = English; 112 | hasScannedForEncodings = 0; 113 | knownRegions = ( 114 | en, 115 | ); 116 | mainGroup = 0C9E14121BA4790200B6EF0E; 117 | productRefGroup = 0C9E141C1BA4790200B6EF0E /* Products */; 118 | projectDirPath = ""; 119 | projectRoot = ""; 120 | targets = ( 121 | 0C9E141A1BA4790200B6EF0E /* stampicon */, 122 | ); 123 | }; 124 | /* End PBXProject section */ 125 | 126 | /* Begin PBXSourcesBuildPhase section */ 127 | 0C9E14171BA4790200B6EF0E /* Sources */ = { 128 | isa = PBXSourcesBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | 0C9E141F1BA4790200B6EF0E /* main.swift in Sources */, 132 | 0C9E142A1BA4C6A000B6EF0E /* CGRectExtensions.swift in Sources */, 133 | 0C9E14281BA4B73300B6EF0E /* NSColor+Hex.swift in Sources */, 134 | 0C9E14261BA4828900B6EF0E /* Stamper.swift in Sources */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXSourcesBuildPhase section */ 139 | 140 | /* Begin XCBuildConfiguration section */ 141 | 0C9E14201BA4790200B6EF0E /* Debug */ = { 142 | isa = XCBuildConfiguration; 143 | buildSettings = { 144 | ALWAYS_SEARCH_USER_PATHS = NO; 145 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 146 | CLANG_CXX_LIBRARY = "libc++"; 147 | CLANG_ENABLE_MODULES = YES; 148 | CLANG_ENABLE_OBJC_ARC = YES; 149 | CLANG_WARN_BOOL_CONVERSION = YES; 150 | CLANG_WARN_CONSTANT_CONVERSION = YES; 151 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 152 | CLANG_WARN_EMPTY_BODY = YES; 153 | CLANG_WARN_ENUM_CONVERSION = YES; 154 | CLANG_WARN_INT_CONVERSION = YES; 155 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 156 | CLANG_WARN_UNREACHABLE_CODE = YES; 157 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 158 | COPY_PHASE_STRIP = NO; 159 | DEBUG_INFORMATION_FORMAT = dwarf; 160 | ENABLE_STRICT_OBJC_MSGSEND = YES; 161 | ENABLE_TESTABILITY = YES; 162 | GCC_C_LANGUAGE_STANDARD = gnu99; 163 | GCC_DYNAMIC_NO_PIC = NO; 164 | GCC_NO_COMMON_BLOCKS = YES; 165 | GCC_OPTIMIZATION_LEVEL = 0; 166 | GCC_PREPROCESSOR_DEFINITIONS = ( 167 | "DEBUG=1", 168 | "$(inherited)", 169 | ); 170 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 171 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 172 | GCC_WARN_UNDECLARED_SELECTOR = YES; 173 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 174 | GCC_WARN_UNUSED_FUNCTION = YES; 175 | GCC_WARN_UNUSED_VARIABLE = YES; 176 | MACOSX_DEPLOYMENT_TARGET = 10.10; 177 | MTL_ENABLE_DEBUG_INFO = YES; 178 | ONLY_ACTIVE_ARCH = YES; 179 | SDKROOT = macosx; 180 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 181 | }; 182 | name = Debug; 183 | }; 184 | 0C9E14211BA4790200B6EF0E /* Release */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 189 | CLANG_CXX_LIBRARY = "libc++"; 190 | CLANG_ENABLE_MODULES = YES; 191 | CLANG_ENABLE_OBJC_ARC = YES; 192 | CLANG_WARN_BOOL_CONVERSION = YES; 193 | CLANG_WARN_CONSTANT_CONVERSION = YES; 194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 195 | CLANG_WARN_EMPTY_BODY = YES; 196 | CLANG_WARN_ENUM_CONVERSION = YES; 197 | CLANG_WARN_INT_CONVERSION = YES; 198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 199 | CLANG_WARN_UNREACHABLE_CODE = YES; 200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 201 | COPY_PHASE_STRIP = NO; 202 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 203 | ENABLE_NS_ASSERTIONS = NO; 204 | ENABLE_STRICT_OBJC_MSGSEND = YES; 205 | GCC_C_LANGUAGE_STANDARD = gnu99; 206 | GCC_NO_COMMON_BLOCKS = YES; 207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 209 | GCC_WARN_UNDECLARED_SELECTOR = YES; 210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 211 | GCC_WARN_UNUSED_FUNCTION = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | MACOSX_DEPLOYMENT_TARGET = 10.10; 214 | MTL_ENABLE_DEBUG_INFO = NO; 215 | SDKROOT = macosx; 216 | }; 217 | name = Release; 218 | }; 219 | 0C9E14231BA4790200B6EF0E /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | PRODUCT_NAME = stampicon; 223 | }; 224 | name = Debug; 225 | }; 226 | 0C9E14241BA4790200B6EF0E /* Release */ = { 227 | isa = XCBuildConfiguration; 228 | buildSettings = { 229 | PRODUCT_MODULE_NAME = stampicon; 230 | PRODUCT_NAME = stampicon; 231 | }; 232 | name = Release; 233 | }; 234 | /* End XCBuildConfiguration section */ 235 | 236 | /* Begin XCConfigurationList section */ 237 | 0C9E14161BA4790200B6EF0E /* Build configuration list for PBXProject "stampicon" */ = { 238 | isa = XCConfigurationList; 239 | buildConfigurations = ( 240 | 0C9E14201BA4790200B6EF0E /* Debug */, 241 | 0C9E14211BA4790200B6EF0E /* Release */, 242 | ); 243 | defaultConfigurationIsVisible = 0; 244 | defaultConfigurationName = Release; 245 | }; 246 | 0C9E14221BA4790200B6EF0E /* Build configuration list for PBXNativeTarget "stampicon" */ = { 247 | isa = XCConfigurationList; 248 | buildConfigurations = ( 249 | 0C9E14231BA4790200B6EF0E /* Debug */, 250 | 0C9E14241BA4790200B6EF0E /* Release */, 251 | ); 252 | defaultConfigurationIsVisible = 0; 253 | defaultConfigurationName = Release; 254 | }; 255 | /* End XCConfigurationList section */ 256 | }; 257 | rootObject = 0C9E14131BA4790200B6EF0E /* Project object */; 258 | } 259 | -------------------------------------------------------------------------------- /stampicon.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | --------------------------------------------------------------------------------