Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Image.renderText() in Xcode 12 beta 5 #32

Merged
merged 3 commits into from Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 12 additions & 5 deletions Sources/SwiftGD/Image.swift
Expand Up @@ -118,21 +118,28 @@ public class Image {
public func renderText(
_ text: String, from: Point, fontList: [String], color: Color, size: Double, angle: Angle = .zero
) -> (upperLeft: Point, upperRight: Point, lowerRight: Point, lowerLeft: Point) {
/// Notes on `gdImageStringFT`:
/// - it returns an Tuple of empty `Point`s if there is nothing to render or no valid fonts
/// - `gdImageStringFT` accepts a semicolon delimited list of fonts.
/// - `gdImageStringFT` expects pointers to `text` and `fontList` values
guard !text.isEmpty,
!fontList.isEmpty,
var textCChar = text.cString(using: .utf8),
var joinedFonts = fontList.joined(separator: ";").cString(using: .utf8) else {
return (upperLeft: .zero, upperRight: .zero, lowerRight: .zero, lowerLeft: .zero)
}
let red = Int32(color.redComponent * 255.0)
let green = Int32(color.greenComponent * 255.0)
let blue = Int32(color.blueComponent * 255.0)
let alpha = 127 - Int32(color.alphaComponent * 127.0)
let internalColor = gdImageColorAllocateAlpha(internalImage, red, green, blue, alpha)
defer { gdImageColorDeallocate(internalImage, internalColor) }

// `gdImageStringFT` accepts a semicolon delimited list of fonts.
let fontList = fontList.joined(separator: ";")

// `gdImageStringFT` returns the text bounding box, specified as four
// points in the following order:
// lower left, lower right, upper right, and upper left corner.
// upper left, upper right, lower right, and lower left corner.
Copy link
Contributor

@zntfdr zntfdr Aug 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the update!

This edit is not correct:

  • renderText returns upper left, upper right, lower right, and lower left corner.
  • gdImageStringFT returns lower left, lower right, upper right, and upper left corner.

The comment is about gdImageStringFT.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zntfdr @twostraws My apologies. I have a new patch ( #33 ) in to address the mistake.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcritz Thank you for the quick patch ❤️

var boundingBox: [Int32] = .init(repeating: .zero, count: 8)
gdImageStringFT(internalImage, &boundingBox, internalColor, fontList, size, -angle.radians, Int32(from.x), Int32(from.y), text)
gdImageStringFT(internalImage, &boundingBox, internalColor, &joinedFonts, size, -angle.radians, Int32(from.x), Int32(from.y), &textCChar)

let lowerLeft = Point(x: boundingBox[0], y: boundingBox[1])
let lowerRight = Point(x: boundingBox[2], y: boundingBox[3])
Expand Down
67 changes: 67 additions & 0 deletions Tests/SwiftGDTests/ImageTests.swift
@@ -0,0 +1,67 @@
import XCTest
@testable import SwiftGD

class TestImage: XCTestCase {
/// Trying to create a list that *should* have at least
/// one font for most Swift-compatible platforms.
/// NOTE: Tests might fail on platforms that don’t have
/// one of these fonts installed. E.g.: Docker images
internal static let fontList = [
"SFCompact",
"ArialMT",
"Arial",
"Roboto",
"Ubuntu",
"Noto",
"Noto Sans",
"SSTPro-Roman"
]

func testRenderText() throws {
guard let image = Image(width: 640, height: 480) else {
throw Error.invalidImage(reason: "Could not initialize image")
}
let basepoint = Point(x: 320, y: 240)
let renderBounds = image.renderText(
"SwiftGD",
from: basepoint,
fontList: Self.fontList,
color: .red,
size: 50,
angle: .degrees(-15)
)

XCTAssertFalse(try isEmptyBounds(for: renderBounds), "When text is rendered, it returns NON-zero Points")
}

func testRenderEmptyText() throws {
guard let image = Image(width: 640, height: 480) else {
throw Error.invalidImage(reason: "Could not initialize image")
}
let renderBounds = image.renderText("", from: .zero, fontList: ["Arial", "Ubuntu", "Roboto"], color: .black, size: 18.0)

XCTAssertTrue(try isEmptyBounds(for: renderBounds), "Empty `text` values return tuple of zero-value Points")
}

func testRenderEmptyFontList() throws {
guard let image = Image(width: 640, height: 480) else {
throw Error.invalidImage(reason: "Could not create image")
}
let renderBounds = image.renderText("Hello, World", from: .zero, fontList: [], color: .white, size: 18.0)

XCTAssertTrue(try isEmptyBounds(for: renderBounds), "Empty fontLists return tuple of zero-value Points")
}
}

extension TestImage {
private func isEmptyBounds(for resultValues: (upperLeft: Point, upperRight: Point, lowerRight: Point, lowerLeft: Point)) throws -> Bool {
return [
resultValues.upperLeft,
resultValues.upperRight,
resultValues.lowerRight,
resultValues.lowerLeft
].allSatisfy {
$0 == .zero
}
}
}