From 268d7020f4e5bc9849c9aad30064eeb43a7b6210 Mon Sep 17 00:00:00 2001 From: Domas Nutautas Date: Fri, 24 Jul 2020 15:59:02 +0300 Subject: [PATCH] Support text drawing --- Sources/ImageMod/NSAttributedString.swift | 54 +++++++++++++++++++ .../NSAttributedStringTests.swift | 33 ++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 Sources/ImageMod/NSAttributedString.swift create mode 100644 Tests/ImageModTests/NSAttributedStringTests.swift diff --git a/Sources/ImageMod/NSAttributedString.swift b/Sources/ImageMod/NSAttributedString.swift new file mode 100644 index 0000000..24fa4d5 --- /dev/null +++ b/Sources/ImageMod/NSAttributedString.swift @@ -0,0 +1,54 @@ +import UIKit + +extension NSAttributedString: ImageModable { + + public var mod: ImageMod { + ImageMod(info: .init(size()), + draw: { info in + self.with(info.tint) + .with(info.drawRect.size) + .with(info.shadow) + .draw(in: info.drawRect) + }) + } + + private func with(_ tint: UIColor?) -> NSAttributedString { + guard let tint = tint else { return self } + return with(.foregroundColor, tint) + } + + private func with(_ size: CGSize) -> NSAttributedString { + + let currentSize = self.size() + let scale = min(size.width / currentSize.width, size.height / currentSize.height) + + // https://developer.apple.com/documentation/foundation/nsattributedstring + let defaultFont = UIFont(name: "Helvetica", size: 12)! + let currentFont = attribute(.font, at: 0, effectiveRange: nil) as? UIFont ?? defaultFont + + let font = currentFont.withSize(currentFont.pointSize * scale) + + return with(.font, font) + } + + private func with(_ shadow: (offset: CGPoint, blur: CGFloat, color: UIColor)?) -> NSAttributedString { + + guard let shadow = shadow else { return self } + + let nsShadow = NSShadow() + nsShadow.shadowOffset = CGSize(width: shadow.offset.x, height: shadow.offset.y) + nsShadow.shadowBlurRadius = shadow.blur + nsShadow.shadowColor = shadow.color + + return with(.shadow, nsShadow) + } + + private func with(_ key: NSAttributedString.Key, _ value: Any) -> NSAttributedString { + + let mutable = NSMutableAttributedString(attributedString: self) + mutable.addAttribute(key, value: value, + range: NSRange(location: 0, length: string.utf16.count)) + + return mutable + } +} diff --git a/Tests/ImageModTests/NSAttributedStringTests.swift b/Tests/ImageModTests/NSAttributedStringTests.swift new file mode 100644 index 0000000..484412c --- /dev/null +++ b/Tests/ImageModTests/NSAttributedStringTests.swift @@ -0,0 +1,33 @@ +import XCTest +@testable import ImageMod + +final class NSAttributedStringTests: XCTestCase { + + func test_itDraws() { + + let sut = NSAttributedString(string: "Cats!") + .tinted(.blue) + .scaled(times: 4) + .shadowed(offset: CGPoint(x: 5, y: 10), blur: 20, color: .red) + .image + + + let image = UIGraphicsImageRenderer(size: CGSize(width: 152.5, height: 95.5)).image { context in + + let shadow = NSShadow() + shadow.shadowOffset = CGSize(width: 5, height: 10) + shadow.shadowBlurRadius = 20 + shadow.shadowColor = UIColor.red + + let string = NSAttributedString(string: "Cats!", attributes: [ + .foregroundColor: UIColor.blue, + .shadow: shadow, + .font: UIFont(name: "Helvetica", size: 48)! + ]) + + string.draw(at: CGPoint(x: 15, y: 10)) + } + + XCTAssertEqual(sut.pngData(), image.pngData()) + } +}