swiftnsattributedstringnsimagensmutableparagraphstyle

Should NSMutableParagraphStyle.paragraphSpacingBefore add space before paragraphs?


Note: It appears that as of macOS Big Sur/11.6.4, this bug has been fixed. The test script now produces the second image instead of the third image when using paragraphSpacingBefore.

I’m writing a command-line script on macOS that creates an image from text. I want to give some paragraphs extra space above to set them off from the previous paragraph. Due to the way the script works, it will be easier to set attributes on a paragraph that put extra space above it, rather than change the attributes on the previous paragraph. I thought this was what paragraphSpacingBefore on NSMutableParagraphStyle is for.

However, while I can get paragraphSpacing to provide space after the first paragraph, paragraphSpacingBefore appears to add space after the second paragraph.

This very simplified test script will create an image with three paragraphs in it.

With both the paragraphSpacing line and the paragraphSpacingBefore line commented out, there is no extra spacing added between or around the paragraphs:

no paragraph spacing

If the paragraphSpacing line is uncommented, there is space between the three paragraphs:

paragraphSpacing = 32

But if the paragraphSpacing line is commented out and the paragraphSpacingBefore line is uncommented, there is no space between the three paragraphs but there is space added after the third paragraph:

paragraphSpacingBefore = 32

It appears that the spacing at the end is twice the paragraphSpacingBefore value; if I comment out the cheers += line to make it only have two paragraphs, the space after the final paragraph looks to be about halved:

two paragraphs, paragraphSpacingBefore = 32

This doesn’t make sense to me. I would expect the third image (using paragraphSpacingBefore to be very similar to the second image. I am misunderstanding what paragraphSpacingBefore means, and/or I am doing something wrong.

#!/usr/bin/swift
//test paragraphSpacingBefore

import AppKit

//set up text
var cheers = "“What do you say, Norm?”\nAny cheap, tawdry thing that’ll get me a beer."
cheers += "\n—Cheers"

var paragraphStyle = NSMutableParagraphStyle()
//paragraphStyle.paragraphSpacing = 32
paragraphStyle.paragraphSpacingBefore = 32
var textAttributes = [NSAttributedString.Key.paragraphStyle:paragraphStyle]
let textLines = NSAttributedString(string:cheers, attributes:textAttributes)

//create image of text
let outputSize = textLines.size()
let textRect = NSRect(x: 0, y: 0, width: outputSize.width, height: outputSize.height)
let background = NSColor(red:0, green:1, blue:1, alpha:1)

let outputImage = NSImage(size:outputSize, flipped: false) { (outputRect) -> Bool in
    //set a background color
    background.setFill()
    outputRect.fill()

    //draw the text
    textLines.draw(in: textRect)
    return true
}

//save image to file
var imageData = outputImage.tiffRepresentation
let bitmappedText = NSBitmapImageRep(data: imageData!)
imageData = bitmappedText!.representation(using: NSBitmapImageRep.FileType.png, properties:[:])
do {
    try imageData!.write(to: NSURL.fileURL(withPath: "norm.png"))
} catch {
    print("Unable to save file.")
}

If paragraphSpacingBefore should put space above the second and third paragraphs, what am I doing wrong?


Solution

  • From what I observed, this behaviour only appears when you use the swift command:

    swift YourScript.swift
    

    If you instead run the code in an Xcode "Command Line Tool" project, or run the code in an Xcode playground, or compile the code with swiftc, then run it,

    swiftc YourScript.swift && ./YourScript
    

    paragraphSpacingBefore correctly puts the spacing in the right places.

    I suspect that you might need to specify an option to swift for this to work, but I couldn't find a relevant option that is unique to swift.

    So a workaround now is simply to write a new bash/zsh script that runs:

    swiftc YourScript.swift && ./YourScript