Make PDF from NSAttributedString/NSMutableAttributedString?

I have extracted some text as NSMutableAttributedStrings from a PDF, looping through the pages and joining them all together at the end. I did this in order to trim text from each page using RegEx. Now I think I would like to create a new PDF from this AttributedString, but I am having trouble figuring out how. Possible?

I suspect you’ll have to create an NSTextView, add the attributed string, then use dataWithPDFInsideRect: to generate the PDF data.

A cheap trick could be to use the Pasteboard for that, as it can automatically convert between rich text (RTF), HTML and PDF. You put in the text in one format and then retrieve it in the other.

I don’t know the details for doing that in AppleScript, though.

Are you sure? I put some rtf on the pasteboard and tried this:

current application's NSPasteboard's generalPasteboard()'s availableTypeFromArray:{current application's NSPasteboardTypePDF}

The result was missing value.

You’re right. Despite me selling a clipboard recorder and making another clipboard tool (ShowClipboards) I can’t even get this right.

I so often see PDFs in the clipboard for text clips (mostly from Word and Excel that I have to keep looking at because the MS apps keep inserting non-permanent data into the clipboard that must not be recorded), that I right out assumed it would happen all the time.

Correct is that only automatic conversion between rich text and html does happen, but not with PDF.

BTW, I have code for rendering rich text and html into PDF, but that’s written in Xojo code. I’d be happy to share that code with Ian if he wants to give Xojo a try. It’s using the older WebKit framework and converting that to AppleScript is quite a lot of, I suspect. But then again, if it’s just text, using the workaround via a NSTextView is much easier.

Actually, I just checked and I have some code here that does it, using a print operation:

use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

on saveStyledText:styledText asPDFToFile:newPath
	-- create print info for saving to file
	set destURL to current application's NSURL's fileURLWithPath:newPath
	set printInfo to current application's NSPrintInfo's alloc()'s initWithDictionary:(current application's NSDictionary's dictionaryWithObject:destURL forKey:(current application's NSPrintJobSavingURL)) -- sets destination
	printInfo's setJobDisposition:(current application's NSPrintSaveJob) -- save to file job
	printInfo's setHorizontalPagination:(current application's NSClipPagination)
	printInfo's setVerticalPagination:(current application's NSAutoPagination)
	-- get page size and margins
	set pageSize to printInfo's paperSize()
	set theLeft to printInfo's leftMargin()
	set theRight to printInfo's rightMargin()
	set theTop to printInfo's topMargin()
	-- make a very deep text view
	set theView to current application's NSTextView's alloc()'s initWithFrame:{{0, 0}, {(pageSize's width) - theLeft - theRight, 3.0E+38}}
	theView's setHorizontallyResizable:false
	-- put in the text
	theView's textStorage()'s setAttributedString:styledText
	-- size to fit; must be done on the main thread
	if current application's NSThread's isMainThread() then
		theView's sizeToFit()
	else
		theView's performSelectorOnMainThread:"sizeToFit" withObject:(missing value) waitUntilDone:true
	end if
	-- create print operation and run it
	set printOp to current application's NSPrintOperation's printOperationWithView:theView printInfo:printInfo
	printOp's setShowsPrintPanel:false
	printOp's setShowsProgressPanel:false
	if current application's NSThread's isMainThread() then
		set my theResult to printOp's runOperation()
	else
		my performSelectorOnMainThread:"runPrintOperation:" withObject:printOp waitUntilDone:true
	end if
end saveStyledText:asPDFToFile:

on runPrintOperation:printOp -- on main thread
	set my theResult to printOp's runOperation()
end runPrintOperation:
2 Likes

Huh, I had not been aware that I could tell the print system to print to a file.

So, this would then also handle pagination, to the view’s size, I guess?

Exactly. That’s what the NSAutoPagination for verticalPagination does in the code above.

1 Like

Nice code. Thanks!
Is it possible to use some styled text from the clipboard and pass it to the code posted by Shane?
I tried using:

set styledText to the clipboard
set thePDF to my saveStyledText:styledText asPDFToFile:"/Users/ldicroce/Desktop/newPDF.pdf"

but I got this error:
-[__NSCFString string]: unrecognized selector sent to instance 0x7fe13d42ea00

Thanks