From 691f03fd8d42dcd59270b5a890e3dea378b397f1 Mon Sep 17 00:00:00 2001 From: Vincent Isambart Date: Wed, 29 Nov 2023 09:05:42 +0900 Subject: [PATCH] Initial commit --- .github/pull_request_template.md | 3 + .gitignore | 8 + LICENSE | 22 ++ Package.resolved | 14 + Package.swift | 31 +++ README.md | 21 ++ .../Extensions/CoreGraphicsExtensions.swift | 82 ++++++ Sources/Extensions/CoreTextExtensions.swift | 6 + Sources/Gravity.swift | 119 +++++++++ Sources/Resources/alpha_badge_dark.png | Bin 0 -> 7309 bytes Sources/Resources/alpha_badge_light.png | Bin 0 -> 6514 bytes Sources/Resources/beta_badge_dark.png | Bin 0 -> 6386 bytes Sources/Resources/beta_badge_light.png | Bin 0 -> 2897 bytes Sources/Shield.swift | 248 ++++++++++++++++++ Sources/Swadgics.swift | 196 ++++++++++++++ Sources/TextLine.swift | 28 ++ 16 files changed, 778 insertions(+) create mode 100644 .github/pull_request_template.md create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Package.resolved create mode 100644 Package.swift create mode 100644 README.md create mode 100644 Sources/Extensions/CoreGraphicsExtensions.swift create mode 100644 Sources/Extensions/CoreTextExtensions.swift create mode 100644 Sources/Gravity.swift create mode 100644 Sources/Resources/alpha_badge_dark.png create mode 100644 Sources/Resources/alpha_badge_light.png create mode 100644 Sources/Resources/beta_badge_dark.png create mode 100644 Sources/Resources/beta_badge_light.png create mode 100644 Sources/Shield.swift create mode 100644 Sources/Swadgics.swift create mode 100644 Sources/TextLine.swift diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b620438 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,3 @@ +I do not intend to merge pull requests with something else than small bug fixes. + +Do not hesitate to fork this project and adapt it to your own needs. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a369b00 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2019 Daniel Griesser +Copyright (c) 2023 Vincent Isambart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..d429d39 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", + "version" : "1.2.3" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..c4063ed --- /dev/null +++ b/Package.swift @@ -0,0 +1,31 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Swadgics", + // macOS 13 to be able to use `Regex`. + // (It should not be hard to support older versions of macOS using `NSRegularExpression` if needed) + platforms: [.macOS(.v13)], + products: [ + .executable(name: "swadgics", targets: ["Swadgics"]), + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.3"), + ], + targets: [ + .executableTarget( + name: "Swadgics", + dependencies: [ + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ], + resources: [ + .embedInCode("Resources/alpha_badge_dark.png"), + .embedInCode("Resources/alpha_badge_light.png"), + .embedInCode("Resources/beta_badge_dark.png"), + .embedInCode("Resources/beta_badge_light.png"), + ] + ), + ] +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd2ad56 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Swadgics + +Partial reimplementation of [badge](https://github.com/HazAT/badge) in Swift using Core Graphics (Swadgics = Sw(ift) + (b)adg(e) + (Core Graph)ics). + +Its main advantage is that it does not require ImageMagick, GraphicsMagick, or RSVG, and that at runtime it does not use the network. As it uses Core Graphics it needs to be run on macOS, but for iOS applications that should not be much of a limitation. + +Instead of sending pull requests to add new features, do not hesitate to fork it and adapt it to your own needs. + +This project under the MIT license. The files under `Sources/Resources/` have been copied as-is from [badge](https://github.com/HazAT/badge) but were already under MIT license. + +## Differences + +It handles many of the flags of [badge](https://github.com/HazAT/badge) but the generated image will not be exactly the same. + +Also, Swadgics will not automatically search for icons ([badge](https://github.com/HazAT/badge) looks at `./**/*.appiconset/*.{png,PNG}`), and does not take a `--glob` option. You have to give it the path to the images to modify. You can use your shell's glob features of course. + +```console +$ swadgics badge --shield "Version-0.0.3-blue" --dark --shield_geometry "+0+25%" --shield_scale 0.75 path/to/my/icon.png +``` + +WARNING: As in [badge](https://github.com/HazAT/badge), the files are modified in place so make sure to make a copy before applying Swadgics on them. However, for testing purpose, when only once input file is specified, you can use `--output-file` to specify the file path to output to. diff --git a/Sources/Extensions/CoreGraphicsExtensions.swift b/Sources/Extensions/CoreGraphicsExtensions.swift new file mode 100644 index 0000000..efc9d08 --- /dev/null +++ b/Sources/Extensions/CoreGraphicsExtensions.swift @@ -0,0 +1,82 @@ +import CoreGraphics + +extension CGColorSpace { + // Unfortunately, `sRGB` is already taken for the name of the sRGB color space. + static let sRGBColorSpace = CGColorSpace(name: CGColorSpace.sRGB)! + // `ColorSpace` suffix for uniformity. + static let grayscaleColorSpace = CGColorSpaceCreateDeviceGray() +} + +extension CGColor { + /// sRGB color from 3 0-255 8-bit values (plus an optional alpha). + static func sRGB8(_ red: UInt8, _ green: UInt8, _ blue: UInt8, alpha: CGFloat = 1.0) -> CGColor { + CGColor(srgbRed: CGFloat(red) / 0xff, green: CGFloat(green) / 0xff, blue: CGFloat(blue) / 0xff, alpha: alpha) + } +} + +extension CGImage { + var hasAlpha: Bool { + switch alphaInfo { + case .none, .noneSkipLast, .noneSkipFirst: + return false + default: + return true + } + } +} + +extension CGContext { + static func makeGrayscaleContext(size: CGSize) -> CGContext { + CGContext( + data: nil, + width: Int(size.width.rounded(.up)), + height: Int(size.height.rounded(.up)), + bitsPerComponent: 8, + bytesPerRow: 0, + space: CGColorSpace.grayscaleColorSpace, + // `CGImageAlphaInfo` is part of `CGBitmapInfo`. + // Unfortunately, the grayscale color space only seems to support no alpha or alpha only. + // https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB + bitmapInfo: CGImageAlphaInfo.none.rawValue + )! + } + + static func makeSRBGContext(size: CGSize) -> CGContext { + CGContext( + data: nil, + width: Int(size.width.rounded(.up)), + height: Int(size.height.rounded(.up)), + bitsPerComponent: 8, + bytesPerRow: 0, + space: CGColorSpace.sRGBColorSpace, + // `CGImageAlphaInfo` is part of `CGBitmapInfo`. + bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue + )! + } + + /// Add a rounded rect to the current path of the context. + func addRoundedRect(_ rect: CGRect, radius: CGFloat) { + move(to: CGPoint(x: rect.minX, y: rect.midY)) + addArc( + tangent1End: CGPoint(x: rect.minX, y: rect.minY), + tangent2End: CGPoint(x: rect.midX, y: rect.minY), + radius: radius + ) + addArc( + tangent1End: CGPoint(x: rect.maxX, y: rect.minY), + tangent2End: CGPoint(x: rect.maxX, y: rect.midY), + radius: radius + ) + addArc( + tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), + tangent2End: CGPoint(x: rect.midX, y: rect.maxY), + radius: radius + ) + addArc( + tangent1End: CGPoint(x: rect.minX, y: rect.maxY), + tangent2End: CGPoint(x: rect.minX, y: rect.midY), + radius: radius + ) + closePath() + } +} diff --git a/Sources/Extensions/CoreTextExtensions.swift b/Sources/Extensions/CoreTextExtensions.swift new file mode 100644 index 0000000..e96fca0 --- /dev/null +++ b/Sources/Extensions/CoreTextExtensions.swift @@ -0,0 +1,6 @@ +import CoreText + +extension CTFont { + var ascent: CGFloat { CTFontGetAscent(self) } + var descent: CGFloat { CTFontGetDescent(self) } +} diff --git a/Sources/Gravity.swift b/Sources/Gravity.swift new file mode 100644 index 0000000..4d645a4 --- /dev/null +++ b/Sources/Gravity.swift @@ -0,0 +1,119 @@ +// Partial implementation of ImageMagick gravity and geometry concepts. + +import ArgumentParser +import CoreGraphics + +enum Gravity: String, ExpressibleByArgument { + case northWest = "NorthWest" + case north = "North" + case northEast = "NorthEast" + case west = "West" + case center = "Center" + case east = "East" + case southWest = "SouthWest" + case south = "South" + case southEast = "SouthEast" + + func objectCenter(objectSize: CGSize, canvasSize: CGSize, geometry: Geometry?) -> CGPoint { + var centerX, centerY: CGFloat + switch self { + case .northWest, .west, .southWest: + centerX = 0 + case .north, .center, .south: + centerX = canvasSize.width / 2 + case .northEast, .east, .southEast: + centerX = canvasSize.width + } + switch self { + case .southWest, .south, .southEast: + centerY = 0 + case .west, .center, .east: + centerY = canvasSize.height / 2 + case .northWest, .north, .northEast: + centerY = canvasSize.height + } + + if let geometry { + switch geometry.x { + case .pixels(let x): + centerX += x + case .percent(let x): + centerX += canvasSize.width * x / 100 + } + // Geometry's `y` is inverted compared to Core Graphics + switch geometry.y { + case .pixels(let y): + centerY += -y + case .percent(let y): + centerY += canvasSize.width * -y / 100 + } + } + + let halfObjectSize = CGSize( + width: objectSize.width / 2, + height: objectSize.height / 2 + ) + + if objectSize.width <= canvasSize.width { + if centerX - halfObjectSize.width < 0 { + centerX = halfObjectSize.width + } else if centerX + halfObjectSize.width > canvasSize.width { + centerX = canvasSize.width - halfObjectSize.width + } + } else { + switch self { + case .northWest, .west, .southWest: + centerX = halfObjectSize.width + case .north, .center, .south: + centerX = canvasSize.width / 2 + case .northEast, .east, .southEast: + centerX = canvasSize.width - halfObjectSize.width + } + } + + if objectSize.height <= canvasSize.height { + if centerY - halfObjectSize.height < 0 { + centerY = halfObjectSize.height + } else if centerY + halfObjectSize.height > canvasSize.height { + centerY = canvasSize.height - halfObjectSize.height + } + } else { + switch self { + case .southWest, .south, .southEast: + centerY = halfObjectSize.height + case .west, .center, .east: + centerY = canvasSize.height / 2 + case .northWest, .north, .northEast: + centerY = canvasSize.height - halfObjectSize.height + } + } + return CGPoint(x: centerX, y: centerY) + } +} + +struct Geometry: ExpressibleByArgument { + var x: Offset + var y: Offset + + enum Offset { + case pixels(CGFloat) + case percent(CGFloat) + } + + private static let geometryRegex = try! Regex(#"([+\-]\d+)(%?)([+\-]\d+)(%?)"#, as: (Substring, Substring, Substring, Substring, Substring).self) + + init?(argument: String) { + guard let match = try! Self.geometryRegex.wholeMatch(in: argument) else { return nil } + let (_, x, percentX, y, percentY) = match.output + if percentX.isEmpty { + self.x = .pixels(Double(x)!) + } else { + self.x = .percent(Double(x)!) + } + if percentY.isEmpty { + self.y = .pixels(Double(y)!) + } else { + self.y = .percent(Double(y)!) + } + } +} diff --git a/Sources/Resources/alpha_badge_dark.png b/Sources/Resources/alpha_badge_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..1da5de9570fa0e61f422797986452ae03455d304 GIT binary patch literal 7309 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 Xw8~%dO8Nu-PlWWe3^i*s?4JA|z$Ods literal 0 HcmV?d00001 diff --git a/Sources/Resources/alpha_badge_light.png b/Sources/Resources/alpha_badge_light.png new file mode 100644 index 0000000000000000000000000000000000000000..649dc82fcebfa048a2405d2644d065bfa678904d GIT binary patch literal 6514 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 literal 0 HcmV?d00001 diff --git a/Sources/Resources/beta_badge_dark.png b/Sources/Resources/beta_badge_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..598b961f187b901b246d477f84c08035072a5398 GIT binary patch literal 6386 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