Make Your App's Macros Display Properly in Exports
By default, Scroll PDF Exporter will export most third-party macros without issue.
However, occasionally some third-party macros will not be exported correctly. Normally, this is because the macro has been designed to be displayed in Confluence which behaves differently from a PDF file. Therefore, some macros can be displayed differently in the export because:
- The dimensions do not match – a Confluence page is presented in a landscape format, whereas a PDF file is normally in a portrait orientation.
- The browser for a Confluence page displays scroll bars – a PDF file cannot display this information.
- The macro may lazy-load some data in the browser using a REST interface in order to speed up the original page loading times in Confluence, or because the user first needs to interact with it
This article describes how you, as an app developer, can make sure your macros display properly in PDF exports.
Required steps
To avoid macros being displayed incorrectly, developers must ensure that macros are adaptable so that they are properly displayed in both the Confluence page view and the exported PDF file. This can be achieved by the macro using context information provided by Confluence and Scroll PDF Exporter in order to dynamically switch its output mode to the appropriate format.
Implement a different output mode
All Scroll Exporters render content in the PDF output mode. Therefore a macro can switch it's output like this:
@Override
public String execute(Map<String, String> parameters, String body, ConversionContext context) {
if (ConversionContextOutputType.PDF.value().equalsIgnoreCase(context.getOutputType())) {
// Render a static table that does not need to load data from a REST interface
return "<table>...</table>";
} else {
// Render just a placeholder for the table that can load the data from some REST interface
return "<div data-context-id='...'/>";
}
}
Context information
In order to further differentiate exports done with Scroll Exporters from those done with Confluence's built-in exporters, we provide some properties in the macro's ConversionContext
:
The following properties are currently available. Both key and values are always of type String
.
Key | Value |
---|---|
com.k15t.scroll.product.key | The full key of the exporter add-on, e.g. com.k15t.scroll.scroll-pdf |
com.k15t.scroll.product.version | The version of the exporter add-on, e.g. 4.0.6 |
com.k15t.scroll.exporter.template.name | The name of the template that is being used for the current export |
These properties can be used like this:
@Override
public String execute(Map<String, String> parameters, String body, ConversionContext context) {
if ("com.k15t.scroll.scroll-pdf".equals(context.getProperty("com.k15t.scroll.product.key"))) {
// Output content specific for Scroll PDF Exporter
return "<table>...</table>";
} else {
// Output generic content
return "<div data-context-id='...'/>";
}
}
Accessing ThreadLocals / HttpServletRequest
Macros often use ‘Thread Locals’ managed by Confluence to share information across multiple macro invocations for a given page / blog post.
One typical approach is to use ServletActionContext.getRequest() or ServletContextThreadLocal.getRequest() to retrieve the current HttpServletRequest instance and then set or retrieve attributes on it. This is currently only partially supported in exports with Scroll Exporters for technical reasons: ServletContextThreadLocal does work but ServletActionContext does not work in Confluence 8.0 and later.
In general we are moving away from providing access to the current HttpServletRequest instance in the scope of exports because export jobs are executed similarly to Confluence’s long running tasks and providing a copy of the current HttpServletRequest inside long running tasks is technically challenging. Additionally, this approach is error prone because these request instances are not intended to be used as a long-lived data containers. Furthermore, Confluence’s native long running tasks do also not provide access to the current HttpServletRequest, so dropping support in our Scroll Exporter apps will align the behaviour with Confluence’s exports and other long running tasks.
Use ThreadLocalCache Instead
If you need to cache information or share it between macro invocations please use Confluence’s ThreadLocalCache / ThreadLocalCacheAccessor instead. Both our Scroll Exporter apps, and Confluence long running tasks, ensure this particular ThreadLocal is properly set up in Scroll Exporter export jobs, Confluence long running tasks, and standard Confluence page rendering calls (viewpage.action invocations etc.). Therefore using ThreadLocalCache will also improve your macro's compatibility with standard Confluence features.