PDF report linking

You completely nerd-sniped me with this topic - I definitely shouldn’t have spent as much time on this as I did. But, this is actually possible with the existing reporting module. We’ll definitely have to see about adding this capability in a better way.
Basically, the workaround I came up with relies on the fact that objects in reporting can be copied to the clipboard and show up as XML representations of the underlying Java object. So, to use this (extremely janky) workaround:
0. Paste the script below into the script console and execute it. Nothing will happen - that’s intentional.

  1. Copy the desired component (I’ve only really tested text shapes) to the clipboard.
  2. Go back to the script console, and in the interactive interpreter, run addURLToClipboardObject(<someURL>). To link to a specific page, use addURLToClipboardObject("Page:3"). You can also use Page:Next and Page:Back.
  3. Click back to the reporting workspace, and specifically click into the editor/visible page. Then paste your modified object.

If everything went correctly, the hyperlink should be active on the preview tab:

And, without further ado, the hacky monstrosity that will do all this. It’s pretty unlikely this will break any existing report, but it may throw all kinds of errors into the console. I would recommend copying your report to a new project, going through this process to add the links, and once you’ve verified it works copy the report back to your original project.

def addURLToClipboardObject(url):
	from java.awt import Toolkit
	from java.awt.datatransfer import DataFlavor, Transferable
	from java.io import ByteArrayInputStream
	from java.lang import String
	import re
	
	class MockReportMillClipboard(Transferable):
		RMDataFlavor = DataFlavor("application/reportmill", "ReportMill Shape Data")
		def __init__(self, data):
			self.data = String(data).getBytes()
		
		def isDataFlavorSupported(self, flavor):
			return flavor == self.RMDataFlavor
		
		def getTransferData(self, flavor):
			if flavor == self.RMDataFlavor:
				return ByteArrayInputStream(self.data)

		def getTransferDataFlavors(self):
			return self.RMDataFlavor.getTransferDataFlavors()
	        
	clip = Toolkit.getDefaultToolkit().getSystemClipboard()
	
	dataflavor = None
	for df in clip.getAvailableDataFlavors():
		if df.mimeType == 'application/x-java-serialized-object; class=java.lang.String':
			dataflavor = df
			break
	if dataflavor:
		data = clip.getData(dataflavor)
		regex = r"<text(.*)>"
		subst = r'<text\g<1> url="%s">' % url
		result = re.sub(regex, subst, data, re.MULTILINE)
		print "Added URL to clipboard object"
		clip.setContents(MockReportMillClipboard(result), None)
3 Likes