2

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?

Jerry Stratton
  • 3,287
  • 1
  • 22
  • 30
  • 1
    I can't reproduce on Xcode playgrounds. If I comment `paragraphSpacing` and uncomment `paragraphSpacingBefore`, I get something similar to your second image, rather than the third. – Sweeper Feb 23 '21 at 04:06
  • 1
    But... if I run it in the terminal, it does produce your third image... – Sweeper Feb 23 '21 at 04:08
  • Ah, thanks. I’ve clarified that this is a command-line script on macOS. But nice to hear that what I want should, then, be possible. – Jerry Stratton Feb 23 '21 at 04:12
  • 1
    I have also tried to create an Xcode project using the "command line tool" template, and the output executable created from works as intended in the command line. How about doing that instead? – Sweeper Feb 23 '21 at 04:15
  • That (Xcode makes it work) is helpful, too. But I would like to know what I’m doing wrong; and I’m not sure the feature is useful enough to forego being able to make quick changes and additions to the script. – Jerry Stratton Feb 23 '21 at 04:19
  • 1
    Compiling in the command line with `swiftc` first, then running the executable works too... This seems to me like a bug in `/usr/bin/swift` (though it's hard to imagine how)... So for now, you can write another script that just does `swiftc YourScript.swift && ./YourScript` :) – Sweeper Feb 23 '21 at 04:27
  • Just tested on the bigger script this was distilled from. That (swiftc) is a very doable workaround until I figure out what's wrong/the bug gets fixed. – Jerry Stratton Feb 23 '21 at 04:38

1 Answers1

1

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
Sweeper
  • 213,210
  • 22
  • 193
  • 313