PDFreactor Migration Guide

Overview

Migrating from PDFreactor 11 to PDFreactor 12

Common Changes

This section lists changes that are relevant to both PDFreactor Library and PDFreactor Web Service integrations.

Security Related Changes

Non-Local File URLs

By default PDFreactor now blocks all file URLs with any non-empty authority components other than exactly "localhost". This is to prevent Java-specific behavior which could be used by potentially malicious authors to gain information about the server's OS or available network resources.

Examples of file URLs and how they are now treated by PDFreactor 12:

  • file:///dir/filevalid
  • file://host/dir/fileinvalid
  • file://localhost/dir/filevalid
  • file://LOCALHOST/dir/fileinvalid
  • file://localhost:8080/dir/fileinvalid
  • file://john:doe@localhost/dir/fileinvalid
    • To achieve the behavior of PDFreactor versions below 12, you need to allow non-remote file URLs in the security settings. Please refer to the PDFreactor manual for more information on this subject.

Access to PDF Output Options

By default, PDFreactor now prevents access to the PDF output options that can be specified in JavaScript via the ro.pdf object. Attempts to access that object will result in an error. This is to prevent potentially malicious authors from altering critical configuration options, such as PDF encryption or PDF script. To re-enable access, use the allowAuthorApiOverrides security setting.

JavaScript Processing

JavaScript Engine

PDFreactor 12 uses a JavaScript implementation based on the JavaScript engine Oracle GraalJS. Previous versions used Mozilla Rhino as JavaScript engine and that engine can still be enabled via a setting. However, we recommend to only use it as a temporary solution for the rare cases when it supports scripts that GraalJS does not yet handle correctly.

config.setJavaScriptSettings(new JavaScriptSettings()
.setJavaScriptEngine(JavaScriptEngine.RHINO));

Note: The included JavaScript library awesomizr.js now requires the use of GraalJS. However, you can use a previous version of that library if you require Rhino.

JavaScript Enabled by Default

JavaScript processing is now enabled by default. It can be disabled via the API:

config.setJavaScriptSettings(new JavaScriptSettings()
.setDisabled(true));

Required Java Versions for JavaScript Processing

JavaScript processing with the GraalJS engine requires at least Java 17 while the Rhino engine only requires Java 8. (Using the PDFreactor Web Service also raises the requirement to Java 17.)

API Related Changes

Deprecated API

The following configuration properties are deprecated and have either been moved into another object or replaced:

  • allowAnnotations → moved to encryptionSettings
  • allowAssembly → moved to encryptionSettings
  • allowCopy → moved to encryptionSettings
  • allowDegradedPrinting → moved to encryptionSettings
  • allowFillIn → moved to encryptionSettings
  • allowModifyContents → moved to encryptionSettings
  • allowPrinting → moved to encryptionSettings
  • allowScreenReaders → moved to encryptionSettings
  • authenticationCredentials → moved to networkSettings and changed data type (see below)
  • cookies → moved to networkSettings
  • encryption → moved to encryptionSettings and renamed to type
  • httpsMode → replaced by securitySettings.trustAllConnectionCertificates (see below)
  • mergeDocuments → moved to mergeSettings and renamed to documents
  • mergeMode → moved to mergeSettings and renamed to mode
  • overlayRepeat → moved to mergeSettings
  • ownerPassword → moved to encryptionSettings
  • rawCookies → obsolete; use networkSettings.cookies instead
  • requestHeaders → moved to networkSettings
  • resourceConnectTimeout → moved to networkSettings and renamed to connectTimeout
  • resourceReadTimeout → moved to networkSettings and renamed to readTimeout
  • userPassword → moved to encryptionSettings

Authentication Credentials

PDFreactor 12 allows you to set multiple credentials in the authenticationCredentials property instead of just one username and password. The new authenticationCredentials property now takes an array of HttpCredentials objects (instead of a single KeyValuePair). Besides the familiar username and password properties, these objects have additional properties to specify e.g. for which scheme and realm they are intended to be used. Please refer to the PDFreactor manual for more information.

HTTPS Mode

The configuration property httpsMode has been replaced by trustAllConnectionCertificates in securitySettings due to its potential impact on server security. The new property now takes a simple boolean parameter which indicates whether PDFreactor should ignore all certificate-related issues, effectively achieving the same behavior as the old httpsMode with LENIENT argument.

New Log Levels

PDFreactor 12 now uses custom log levels consistently throughout its API and in the logs.

Old Log Level used in API Old Log Level used in Logs New Log Level
FATAL
SEVERE
FATAL
ERROR
WARN
WARNING
WARN
INFO
INFO
INFO
DEBUG
CONFIG
DEBUG
DEBUG
FINE
DEBUG
FINER
PERFORMANCE
FINEST
TRACE

Note that log level TRACE in particular should never be used in production as it may produce a large amount of records and enable additional analysis features which may impact performance.

Document Type Detection

The automatic document type detection now takes file extensions, XML declarations and certain elements of the doctype preamble into account to find a suitable document type, and thus the detection decides which document parser should be used.

If a different parser is used and this causes unwanted changes, then the document type has to be forced to the previously used type via the configuration property documentType. The used document type can be found in the PDFreactor log on the level INFO.

Color Space Conversion for Image Output

Conversion to CMYK is now supported for image output. This also means that for image formats that don't support CMYK an exception will be thrown when CMYK conversion is specified via the ColorSpaceSettings.

config.setColorSpaceSettings(null);

Barcode API

DataBar Barcodes

The values of -ro-barcode-type were changed, regular GS1 DataBar (Omnidirectional) barcodes are now created using databar omnidirectional instead of databar normal and GS1 DataBar Stacked Omnidirectional barcodes are now created using databar omnidirectional-stacked instead of databar omnidirectional. If either of these two values are used, the stylesheets need to be updated accordingly. The default subtype is unchanged, so if the second argument is omitted, no change is required.

USPS OneCode Barcodes

In previous versions, USPS OneCode Barcodes did not have any human-readable text but do now by default. It can be deactivated with -ro-barcode-human-readable-position.

-ro-barcode-human-readable-position: none;
The natural size was also increased, so some changes to the surrounding layout may be required.

Layout Changes

Visibility of noscript Elements

The HTML element noscript is now handled properly and will be made visible when JavaScript has been disabled, instead of being always set to display:none. In order to restore the previous behavior, the display style of the elements has to be set manually:

noscript {
    display: none;
}

Fragmentation of Flex Containers with Height

Multi-line flex containers with a non-auto height no longer prevent fragmentation and are now allowed to be split between their flex lines (just like flex containers with an auto height). The previous behavior can be restored by setting overflow: scroll on the flex container, thus making the flex container monolithic and preventing any breaks:

.flex {
    overflow: scroll;
}

Note that while there are no scrollbars in the resulting PDF, browsers would display them, so this style should only be applied during the conversion. When setting the style via the PDFreactor API is not an option, this can be achieved in style sheets by using the media rule @media -ro-pdfreactor.

Default Vertical Align in Fast Tables

PDFreactor 12 adds support for the vertical-align property to Fast Tables, specifically for the values bottom and middle, treating all others as top. This means that the default value for HTML table cells, i.e. middle, is now respected, changing the default alignment visibly when cell heights are larger than the font heights.

table.fastTable td,
table.fastTable th {
    vertical-align: top;
}

New Media Feature Default Values

Some default values that PDFreactor uses to resolve media features have been changed to more accurately represent the output:

  • resolution has been changed from 2dppx to 300dpi
  • width and height have been changed to be based on A4 with a 2cm margin instead of only 1cm, i.e. a change from 190 × 277mm to 170 × 257mm.
  • color was previously always 8 and now depends on whether a grayscale or monochrome image output has been specified, in which case it is set to 0.
  • color-index was previously always 0, but is now 256 if the output has been set to the image format gif.
  • monochrome was previously always 0, but is now 8 when forceGrayscaleImage has been set and 1 if a monochrome tiff variant has been chosen as output format.

Should any problems arise from these new values, the previous ones can be restored via the API. For example:

config.setMediaFeatureValues(
    new MediaFeatureValue().setMediaFeature(MediaFeature.RESOLUTION).setValue("2dppx"));

Absolutely Positioned Elements inside Multi-column

In previous versions, the containing block of elements with position:absolute inside a multi-column element was the column itself, thus placing it relative to the column. In PDFreactor 12 the columns are no longer containing blocks for absolutely positioned elements, as is demanded by the CSS specifications.

The new value column has been introduced for the proprietary property -ro-position-origin which allows to restore the original behavior:

.pos-abs {
    -ro-position-origin: column;
}

Fragmentation of Table Captions

Table captions can not be distributed over multiple fragmentation containers (pages, columns, etc.) anymore. They are now monolithic and considered as a coherent unit. Forced breaks inside them do not have an effect anymore. The behavior of PDFreactor 11 can not be restored via pure CSS. If the old behavior is necessary then the table caption element should be removed from the table and placed before or after the table and the display value of the caption should be set to block.

Baseline of Images

The baseline of images is now the bottom margin edge, instead of the bottom border edge.

Image Clipping Paths are Now Disabled by Default

To match the browser behavior, any clip path stored in the meta data of images is now ignored by default. To reactivate them, the proprietary CSS property -ro-image-clip-path can be set to auto or from-image.

img, .background-image {
    -ro-image-clip-path: auto;
}

Footnotes on column- or region-level

Footnotes are now always placed on page-level by default. In order to place footnotes on column- or region-level the property -ro-float-reference can be used.

.multiColumn .footnote {
    -ro-float-reference: column;
}
.regionElement .footnote {
    -ro-float-reference: region;
}

Default Image Interpolation

Unless prohibited by conformance, image interpolation is now enabled by default. For PDF output this differs from previous behavior where the interpolation flag was never set. The previous behavior can be restored using the property image-rendering:

* {
    image-rendering: pixelated;
}

CSS Related Changes

Comment Properties

To create PDF comments, you now have to specify at least one of the following CSS properties: -ro-comment-content, -ro-comment-style, -ro-comment-start, or -ro-comment-end. In the rare case where you want to create comments and don't need either of these properties, it is recommended to specify an empty string as the value for -ro-comment-content to create the comment:

.comment {
    -ro-comment-content: "";
}

Inherited Rasterization Properties

The properties -ro-rasterization, -ro-rasterization-supersampling and -ro-rasterization-max-size are now inherited. So to e.g. not increase the resolution of an SVG in a paragraph with text shadow you have to to reset the value for that SVG:

<p>Hello <i>World</i> <img src="some.svg"></p>
p {
    text-shadow: 2pt 3pt 4pt red;       /* both are inherited ... */
    -ro-rasterization-supersampling: 4; /* ... so they affect i and img as well */
}
img {
    -ro-rasterization-supersampling: initial; /* undo the effect on img */
}

Printer Marks

The default position (offset) of crop marks and color bars has changed. The inner side of both marks now is located at the outer edge of the bleed area (offset = 100% of bleed). In order to undo this change and to obtain the old behavior the following CSS can be used:

@page {
    -ro-colorbar-offset: calc(100% + 5mm / 3); /* 100% bleed + colorBarSize / 3 */
    -ro-crop-mark-offset: 66%;                 /* 66% of bleed */
}

Explicit Value of List Items

The proprietary CSS property -ro-listitem-value has been deprecated. The functionality of this property is to explicitly set the value of a list item. Since PDFreactor 12 list items are based on CSS counters. Because of this the value of list items should from now on be manipulated with the corresponding counter properties counter-set, counter-reset and counter-increment only. For backwards compatibility the -ro-listitem-value property now is a shorthand for the counter-set property. It can still be used but it is recommended to adjust all occurrences similar to the following example:

.listItem10 { -ro-listitem-value: 10 }     /* old */
.listItem10 { counter-set: list-item 10 }  /* new */

Creation of Page Margin Boxes

The creation of page margin boxes is now only possible if the content property is not none. The possibility to create a page margin box via width and height only (and no content property) is no longer possible. If a style sheet contains such page margin box rules then the content property must be added as otherwise the boxes will not be created. The following example demonstrates this:

/* old behavior (no content property is necessary) */
@top-center {
    width: 100%;
    height: 100%;
}

/* new behavior (content property is required) */
@top-center {
    content: "";
    width: 100%;
    height: 100%;
}

Generated content on replaced elements

In order to be compatible with the CSS specification and the behavior of browsers, generated content on images is no longer supported (e.g. "img::before"). If this functionality is still required, it can be simulated using JavaScript by inserting elements before or after a replaced element.

PDF Script Actions Set from CSS

The proprietary CSS property pdf-script-action, applicable in @-ro-preferences, has been removed. You can still set PDF script actions via the API configuration property PdfScriptAction or, if enabled, in JS, using ro.pdf.pdfScriptActions.

Miscellaneous Changes

Links in PDFs Used as Images

PDFs used as images now contain the external links from the source document. To disable this new behavior the following style has been introduced:

.no-links {
    -ro-image-interactivity: none;
}

PDFreactor Library

Please also refer to the common changes which affect the PDFreactor Library as well.

Operational Changes

Artifact Names

PDFreactor traditionally comes in two JAR variants: A monolithic JAR containing all dependencies and a modular JAR without any dependencies. This remains the same in PDFreactor 12 but we renamed the JAR artifacts because we consider the modular JAR now the standard.

Old Name New Name Location
Modular JAR:
pdfreactorcore.jar
pdfreactor.jar
lib
Monolithic JAR:
pdfreactor.jar
pdfreactor-uber.jar
lib/uber-jar

Runtime JVM

Support for Java versions 8 to 16 is deprecated. Future versions of PDFreactor will require Java 17+.

Usage of certain features like JavaScript processing with the new default engine already requires Java 17.

Working Directory of the Command Line Wrapper

The working directory for the PDFreactor command line wrapper (pdfreactor-cmd on macOS and Linux, pdfreactor-cmd.exe on Windows systems) is now the current working directory instead of the directory where the command line wrapper is located.

As such certain paths (such as the PDF output directory) are now resolved relative to the current working directory instead of the command line wrapper location.

API Related Changes

Java Command Line API

In PDFreactor 12, Java Command Line arguments should now be specified in kebab-case notation instead of the previous camel-case notation, i.e. use --disable-bookmarks instead of --disableBookmarks.

The old notation is still supported but is considered deprecated, and will thus be removed in a future version.

In addition, following the deprecation of several configuration properties, their matching command line arguments have been removed. Other arguments have been added to retain the old functionality, but their names now more closely resemble their configuration counterparts. Use the -h argument to get a full list of supported arguments.

  • --allowAnnotations--encryption-allow-annotations
  • --allowAssembly--encryption-allow-assembly
  • --allowCopy--encryption-allow-copy
  • --allowDegradedPrinting--encryption-allow-degraded-printing
  • --allowFillIn--encryption-allow-fill-in
  • --allowModifyContents--encryption-allow-modify-contents
  • --allowPrinting--encryption-allow-printing
  • --allowScreenReaders--encryption-allow-screen-readers
  • --encryption--encryption-type
  • --httpsMode → replaced by -k, --insecure, or --security-trust-all-connection-certificates
  • --mergeDocuments--merge-documents
  • --mergeMode--merge-mode
  • --overlayRepeat--merge-overlay-repeat
  • --ownerPassword--encryption-owner-password
  • --resourceConnectTimeout--network-connect-timeout
  • --resourceReadTimeout--network-read-timeout
  • --userPassword--encryption-user-password

Font Cache

In previous versions, PDFreactor automatically created a file system font cache in the user's home directory if no other location was specified. Depending on the integration scenario, that home directory may not exist. So to achieve consistent behavior, the cache is now disabled by default..

config.setCacheFonts(true);

PDFreactor Web Service

Please also refer to the common changes which affect the PDFreactor Web Service as well.

Operational Changes

Runtime JVM

The PDFreactor Web Service now uses Jetty 12 and requires Java 17 or higher to run.

Servlet Compatibility

The PDFreactor 12 Web Service is now only compatible with Jakarta Servlet 5.0, dropping support for all earlier Java EE Servlet specifications.

Binary Response Content Types

In previous versions, various REST resources of the PDFreactor Web Service responded with the value application/octet-stream for the Content-type header if this type was specifically requested either via Accept header or .bin file extension. In PDFreactor 12, the service will now always use the actual binary media type for the Content-type header, such as application/pdf or image/jpeg, in lieu of the generic application/octet-stream.

Integrators that rely on the Content-type response header's value specifically being application/octet-stream need to adjust their integration since that media type is no longer a possible value.

Location of Custom Config Files

With PDFreactor 12, the directory structure of the installed resources was changed. PDFreactor Web Service-related resources are now located in the jetty-base-pdfreactor subdirectory. To accommodate the changes made by Jetty 12, the jetty directory was renamed to jetty-base-pdfreactor. So if you've made any changes or added custom .ini files, you may have to migrate those files to the following new locations:

  • PDFreactor/jetty/start.ini
    PDFreactor/jetty-base-pdfreactor/start.d/start.ini
  • PDFreactor/jetty/start.d/*.ini
    PDFreactor/jetty-base-pdfreactor/start.d/*.ini

When upgrading an existing PDFreactor installation using an installer, the old PDFreactor/jetty directory will remain and you have to manually migrate any custom config files to PDFreactor/jetty-base-pdfreactor.

API Related Changes

JavaScript Client

The JavsScript Client is now an ES6 module and can be found under module/pdfreactor.js. Further, PDFreactor and other types are now classes.

<script src="lib/PDFreactor.js">
    var pdfReactor = new PDFreactor();
    
    try {
        await pdfReactor.convert(config);
        // ...
    } catch (PDFreactor.PDFreactorWebserviceError e) {
        // ...
    }                        
</script>
<script type="module">
    import { PDFreactor, PDFreactorWebserviceError } from './module/pdfreactor.js';
    
    const pdfReactor = new PDFreactor();
    
    try {
        await pdfReactor.convert(config);
        // ...
    } catch (PDFreactorWebserviceError e) {
        // ...
    }                        
</script>

Please refer to the JavaScript Client samples for more complex integrations.

Should ES6 modules be incompatible with your integration, feel free to use the PDFreactor 11 JavaScript Client, which remains compatible with PDFreactor 12.

Node.js Client

The Node.js Client is now an ES6 module and can be found under module/pdfreactor.mjs. Further, PDFreactor and other types are now classes.

The PDFreactor module now requires Node 18 and relies on the fetch API. It uses the native Node fetch implementation if available, otherwise it falls back on the node-fetch module. So please make sure either one is available. If the native fetch implementation is available, you can omit the module/fetch-polyfill.mjs file.

var pdfReactor = new PDFreactor();

try {
    await pdfReactor.convert(config);
    // ...
} catch (PDFreactor.PDFreactorWebserviceError e) {
    // ...
}
import { PDFreactor, PDFreactorWebserviceError } from './module/pdfreactor.mjs';

const pdfReactor = new PDFreactor();

try {
    await pdfReactor.convert(config);
    // ...
} catch (PDFreactorWebserviceError e) {
    // ...
}

Please refer to the Node.js Client samples for more complex integrations.

Should ES6 modules be incompatible with your integration, feel free to use the PDFreactor 11 Node.js Client, which remains compatible with PDFreactor 12. Alternatively, you can use an ES6 module within a CommonJS module like this:

(async () => {
    const { PDFreactor, PDFreactorWebserviceError } = await import('../module/pdfreactor.mjs');
    const pdfReactor = new PDFreactor();
    
    try {
        await pdfReactor.convert(config);
        // ...
    } catch (PDFreactorWebserviceError e) {
        // ...
    }
})();

PHP Client

PDFreactor 12 comes with a new version of the PHP client (client version 12) which requires cURL to be installed on the client's host system. The old client (client version 8) is compatible with PDFreactor 12 and can still be used, but it may lack certain new features.

Python Client

The Python client (client version 12) now requires Python 3.10. Support for Python 2 was dropped.

Perl Client

PDFreactor 12 no longer contains a Perl Client. You can continue to use the PDFreactor 11 Perl Client with PDFreactor 12, however we no longer offer any support for it from PDFreactor 12 onwards.

Awesomizr

The Awesomizr JavaScript library has been converted to a module and should now be used as such.

<script src="awesomizr.js"></script>
<script>
    window.onload = function() {
        Awesomizr.createTableOfContents(...);
    }
</script>
<script type="module">
    import * as Awesomizr from "./awesomizr.js";
    
    window.onload = () => {
        Awesomizr.createTableOfContents(...);
    }
</script>

To avoid migration, you can still use the non-module version of Awesomizr that was shipped with PDFreactor 11.

Migrating from PDFreactor 11.3 to PDFreactor 11.4

Layout Changes

Clipping of Multi-column Content

In earlier versions of PDFreactor, content inside columns of a multi-column element could not overflow its column, and thus not overlap the content of the next column. Instead it was clipped between the columns. This behavior has been updated to match the browser behavior and the latest CSS specifications, which means that such overflowing content is now allowed.

To restore the legacy behavior, the new proprietary property -ro-column-clip has been introduced, which has to be applied on the multi-column element:

.multi-column-element {
    -ro-column-clip: auto;
}

Updated Column Count Computation of Multi-column

The algorithm to determine the column count of a multi-column element has been updated to the latest specifications. The main difference is that in the earlier version, setting values to both column-count and column-width at the same time caused the column-count value to be ignored. With the new implementation, it acts as a maximum for the column count.

To restore the behavior of the old algorithm for cases where both column-width and column-count have been set, override the value of column-count with auto:

.multi-column-element {
    column-count: auto;
}

Tables with min- and max-widths

PDFreactor 11.4 now supports the min-width and max-width properties on table root elements. If these properties are present in existing documents it can lead to a different layout result. In order to revert such changes the following styles can be used:

table {
    min-width: auto;
    max-width: none;
}

Migrating from PDFreactor 11.x to PDFreactor 11.3

API Related Changes

Asset Packages

In earlier versions of PDFreactor, Asset Package conversions would automatically block certain URL connections, such as connections to local HTTP addresses, and would allow connections to JARs which were in the class path. This behavior was confusing and had nothing to do with Asset Packages. So from PDFreactor 11.3 onwards, only file system access outside of the Asset Package structure is prohibited. If you still want to prohibit connections to local HTTP addresses or if you want to allow access to specific JARs or other files, use PDFreactor's security settings.

Error Policies and Debug Settings

Having any DebugSettings specified in the PDFreactor configuration no longer disables error policies to make sure that the resulting document and the conversion process is altered as little as possible.

Package Related Changes

.NET Standard 2.0 API Path

The location of the The .NET Standard 2.0 API library pdfreactor.dll file changed. It is now located at clients/netstandard2/lib/PDFreactor.dll instead of clients/netstandard2/bin/PDFreactor.dll in the PDFreactor installation package or installation directory.

Migrating from PDFreactor 10 to PDFreactor 11

API Related Changes

Legacy API

The deprecated legacy API in the package com.realobjects.pdfreactor.legacy has been removed. If you are still using the legacy API, refer to the New API migration guide section.

Default Behavior Changes

The creation of PDF bookmarks and links is now enabled by default.

To disable the creation of bookmarks and links, use the following new configuration properties:

  • disableBookmarks
  • disableLinks
config.setDisableBookmarks(true);
config.setDisableLinks(true);

Deprecated API

The following configuration properties have been deprecated and their functionality was moved to other properties:

  • addBookmarksdisableBookmarks
  • addLinksdisableLinks
  • baseURLbaseUrl
  • loggerloggers

Custom URL Stream Handlers

To configure a CustomUrlStreamHandler for all protocols, in PDFreactor 11, use an asterisk as value for the handler's protocol property. Previously, you had to use an empty string, which is now deprecated.

config.setCustomUrlStreamHandlers(
    new CustomUrlStreamHandler()
        .setProtocol("*") // empty string ("") is deprecated
        .setHandler(new URLStreamHandler() {
            // your implementation
        })
);

JavaScript Exports

When exporting an empty string from JavaScript using the ro.exports property, it is now converted to null when accessed through the Result object in the PDFreactor integration.

PDFreactor Web Service Client API Improvements

In previous versions, the PDFreactor Web Service Clients were known as Wrappers or Wrapper-APIs. They are now consistently named clients and their directories in the package have been renamed accordingly.

Also, various methods in the Clients did throw generic exceptions or errors. In PDFreactor 11, these exceptions and errors are now typed and can be explicitly handled in the integration code if so desired.

This does not affect the Java or .NET Clients, as they already threw typed exceptions in previous versions.

JavaScript/Node.js

try {
    // ...
} catch (e) {
    if (e instanceof PDFreactor.PDFreactorWebserviceError) {
        // Since 'e' is now an object, use 'e.message' to access the error message
        // ...
    }
}

PHP

try {
    // ...
} catch (Exception $e) {
} catch (PDFreactorWebserviceException $e) {
    // ...
}

Python

try:
    # ...
except Exception as e:
except PDFreactorWebserviceException as e:
    # ...
}

Ruby

begin
    # ...
rescue Exception => error
rescue PDFreactor::PDFreactorWebserviceError => error
    # ...
}

Perl

eval {
    # ...
} || do {
    if ($e->isa("PDFreactor::PDFreactorWebserviceException")) {
        # ...
    }
}

CSS Related Changes

Case-Sensitivity of Selectors

In previous versions of PDFreactor, CSS selectors were generally case-insensitive. However, this has been changed in order to comply with the latest HTML 5 specifications.

For example, ID and class selectors are now case-sensitive. This means that if such selectors used incorrect casing, these will have to be adjusted or they will no longer be applied as intended.

When using an attribute selector to compare attribute values, this compare is now case-sensitive in certain cases, depending on the attribute name. The selector can be modified with an i, to make it case-insensitive again:

li[class^="a"]   { /*...*/ } /* Matches class="a", but not class="A" */
li[class^="a" i] { /*...*/ } /* Matches both class="a" and class="A" */

Image Orientation

PDFreactor now reads orientation data from images by default and rotates the images accordingly.

:root {
    -ro-image-orientation: none;
}

Absolute Positioning Against Pages

Absolute positioning has been corrected, so when boxes are positioned against their page they are positioned against the pages content box instead of its padding box.

body > div.pos {
    -ro-position-origin: padding-box; 
    position: absolute;
    /* top: ...*/
}

Default Break Styles of HTML Lists

The HTML list elements <ol>, <ul> and <dl> no longer set 'break-before: avoid'.

ol, ul, dl {
    break-before: avoid;
}

Separate Media Feature Values for PDF Output and Preview

The proprietary CSS media feature '-ro-output-format: pdf' no longer matches when the document is viewed in the PDFreactor Preview application, only for actual conversions to PDF. To specify styles that should apply in the Preview App, the new value viewer has been introduced.

/* equals the old behavior of 'pdf' */
@media (-ro-output-format: pdf), (-ro-output-format: viewer) {
    /*...*/
}

Integer Values

CSS properties that accept integer values are now invalid if float numbers are used instead. While in previous versions, a float was accepted as long as it was a whole number (e.g. 'counter-increment: counter 1.0' used to work), this is no no longer the case.

The only way to restore the previous behavior is to disable CSS validation:

config.setCssSettings(new CssSettings().setValidationMode(CssPropertySupport.ALL));

Other Functionality Changes

Barcodes

PDFreactor now includes new proprietary CSS properties for detailed control over barcodes, deprecating the old functionality, which was based on XML. Legacy barcodes still work, but only if the correct namespace was set on the barcode element:

<barcode:barcode xmlns:barcode="http://barcode4j.krysalis.org/ns"
message="123456789012">
    <barcode:ean-13/>
</barcode:barcode>

Also, please note that there is no separate barcode type for GS1-128/EAN128 barcodes. To create them, you need to generate a Code128 barcode and set its encoding to gs1:

.gs1-128 {
    -ro-barcode-type: code128;
    -ro-barcode-encoding: gs1;
}

More information on the new properties can be found in the manual.

New Command Line Interfaces

PDFreactor 8 introduced a new Python-based command line interface (CLI) which replaced the old Java-based CLI. However, the Python CLI was dependent on the PDFreactor Web Service and had some inherent limitations.

PDFreactor 11 again includes a Java-based CLI that can be used without a service. Both this new CLI, as well as the Python one, now use a new API:

API Changes

One major change is that you can only specify simple configuration properties directly (i.e. properties that take string, number, boolean or enum parameters). Complex properties have to be specified in a separate JSON file. This makes the API cleaner and easier to use. There are still shortcuts to add user style sheets and user scripts.

python pdfreactor.py -i in.html -o out.pdf -c style.css -C additionalConfig.json
java -jar pdfreactor.jar -i in.html -o out.pdf -c style.css -C additionalConfig.json

pdfreactor.exe

The pdfreactor.exe file is now an executable for the new Java-based CLI, so integrators relying on the PDFreactor Web Service may have to adjust their integration or use the Python-based CLI via the pdfreactor.py script.

Common CLI API

Below is a quick overview of CLI-specific arguments. The list does not include configuration properties. Please also refer to the documentation of the respective CLI, via the -h parameter.

Arg Description
-h Help
-i Input document
-o Output document
-c Additional CSS (supports: content, file and URL)
-j Additional JavaScript (supports: content, file and URL)
-x Additional XSLT (supports: content, file and URL)
-C Additional configuration in JSON format
-v Verbose logging output
-q Quiet mode (no logging output)
-d Debug mode
-I Inspectable mode
-V Show version
-N Number of runs (Java only)
-W Number of warm-ups (Java only)
-s Server URL (Python only)
-a Treat input as Asset Package (Python only)

Migrating from PDFreactor 10.0 to PDFreactor 10.1

Security Enhancements

PDFreactor now features configurable security behavior to prevent attacks in form of the injection of malicious code from potentially untrusted third parties that produce content which is converted by PDFreactor.

Updated Default Behavior

Because of the default security settings, there are some behavior changes:

Please refer to the PDFreactor manual for a detailed description of the security settings and the default behavior.

You can recreate the previous unsafe behavior by using the following security settings. This also works in the deprecated legacy API. Please note that this is strongly discouraged if you process content from potentially untrusted sources.

config.setSecuritySettings(new SecuritySettings()
    .setAllowExternalXmlParserResources(true)
    .setDefaults(new SecurityDefaults()
        .setAllowFileSystemAccess(true)));

PDFreactor Web Service

PDFreactor Web Service users can configure the security settings via server parameters. Please refer to the manual for more information.

Migrating from PDFreactor 9 to PDFreactor 10

Important!

PDFreactor Web Service users: If you have customized the "start.ini" file of the Jetty server, please see section PDFreactor Web Service Jetty before installing PDFreactor 10.

Updated Default Behavior

CSS Validation

The CSS Validator is now enabled by default. In PDFreactor 9 the validation was disabled, thus invalid values could overwrite valid ones. Now with the validation enabled, invalid values are completely ignored, as if they were not in the style rule.

To disable the Validator, use the API method setCssSettings:

config.setCssSettings(new CssSettings().setValidationMode(CssPropertySupport.ALL));

Rasterization

The images that are produced when rasterizing SVGs and canvas elements have now a maximum size of 2 megapixels by default. If the image would be larger, the resolution is reduced. This way, the used memory and the size of the resulting PDF are reduced. The following snippet disables this limit:

svg, canvas, img, object, embed {
    -ro-rasterization-max-size: none;
}

The property can also be used to increase the max-size to a certain megapixel value:

svg, canvas, img, object, embed {
    -ro-rasterization-max-size: 3.5;
}

MathML

Rendering MathML now requires the import of a MathML library. We recommend MathJax, as it creates better results than the lib PDFreactor used in previous versions.

If the input document can not be modified, the following user scripts can be defined to include and use MathJax. The first script consists of settings for the next one:

  • "roMjPath" must be set to the URL or path to the file MathJax.js, excluding the filename itself.
  • "roMjFile" specifies the name of the main MathJax file. It should should usually be left default.
  • "roMjSvgBlacker" allows to optionally increase the thickness of the fonts used by MathJax.

Please see the comments in the snippet for example values:

roMjPath = "";              // default: "",
                            // examples: "MathJax/", "../../resource/js/mathjax/",
                            // "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/"
roMjFile = "MathJax.js";    // default: "MathJax.js",
                            // examples: "mathjax.js", "mathjaxmod.js"
roMjSvgBlacker = 0;         // default: 0,
                            // examples: 1, 2

The second script uses the values form the first one and inserts the required script elements into the document, so MathJax is loaded and processes all "math" elements. It does not have to be modified.

document.documentElement.firstElementChild.insertAdjacentHTML('beforeend',
    '\u003Cscript type="text/x-mathjax-config">MathJax.Hub.Config(' +
    JSON.stringify({
        jax: ["input/MathML", "output/SVG"],
        extensions: ["mml2jax.js"],
        MathML: { extensions: ["content-mathml.js"] },
        SVG: { blacker: (typeof window.roMjSvgBlacker == "number" &&
            window.roMjSvgBlacker > 0 ? window.roMjSvgBlacker : 0) }
    }) +
    ');\u003C/script>\n' +
    '\u003Cscript type="text/javascript" src="' +
    (window.roMjPath ? window.roMjPath : "MathJax/") +
    (window.roMjPath && !(window.roMjPath + "").endsWith("/") ? "/" : "") +
    (window.roMjFile ? window.roMjFile : "MathJax.js") +
    '">\u003C/script>'
);

text-align

As defined by the CSS3 specifications, 'text-align' is now handled as a shorthand for 'text-align-all' and 'text-align-last'. This means that, if 'text-align-last' is defined before 'text-align', the last line alignment will be overridden.

This can be avoided by using 'text-align-all' instead of 'text-align'.

Furthermore 'text-align-last' now also works if the paragraph has not been set to 'justify'.

Form Fields

The default styles for several form fields was updated to a more modern look. Authors that rely on these default styles should verify their documents. If necessary, the legacy styles can be recreated using CSS.

API Changes

General

signPDF

When digitally signing a PDF using the signPDF configuration property, PDFreactor will now throw an exception and thus fail the conversion if the PDF could not be signed. Appropriate log entries as well as exception messages will provide information as to the reason.

Java (non-Web Service)

In previous versions, the "convert" methods were throwing various exceptions. In PDFreactor 10, this has bee reduced to only a single exception type—the PDFreactorException. Previously thrown exceptions are now wrapped in PDFreactorExceptions and are available as their cause. If you are trying to catch the older exception types in your integration, you will receive compile errors.

To resolve this, just remove the now superfluous catch blocks:

try {
    pdfReactor.convert(config);
} catch (PDFreactorException e) {
    // ...
} catch (IOException e) {
    // ...
} catch (SAXException e) {
    // ...
} catch (TransformerException e) {
    // ...
}

PDFreactor Web Service Jetty

When upgrading to PDFreactor 10 via one of the installers, an existing "start.ini" file will now always be overwritten. If you have made any changes to it, it is highly recommended that you backup these changes before installing PDFreactor 10. The installer will also do a backup of an existing "start.ini" file.

From PDFreactor 10 onward, any customizations of Jetty should no longer be done in the "start.ini" file but instead in the new "main.ini" and other "ini" files located in the "PDFreactor/jetty/start.d" directory.

JavaScript Client

The JavaScript client API was changed. Instead of using callback parameters, the API methods now return Promises.

PDFreactor 9− example:

pdfReactor.convert(config, function(result) {
    processResult(result);
}, function(error) {
    processError(error);
});

PDFreactor 10 example:

try {
    const result = await pdfReactor.convert(config);
    processResult(result);
} catch (error) {
    processError(error);
}

For more information, please refer to the new JavaScript integration examples and the JavaScript API documentation.

Java Client

  1. The class

    com.realobjects.pdfreactor.webservice.client.Log.LogRecord

    has been moved and renamed to

    com.realobjects.pdfreactor.webservice.client.Record
  2. All methods that returned lists now return arrays instead.

.NET Client

  1. The namespace of the class

    RealObjects.PDFreactor.Webservice.Client.Log.Record

    has been changed to

    RealObjects.PDFreactor.Webservice.Client.Record
  2. The property

    ExceedingContent

    of the Result class has been renamed to

    ExceedingContents

Deprecated API

All APIs

The configuration properties appendLog and enableDebugMode are now obsolete. Use debugSettings instead.

Old API New API
Enabling JavaScript
config.setJavaScriptMode(true);
config.setJavaScriptSettings(
  new JavaScriptSettings().setEnabled(true));
Enabling debug mode
config.setEnableDebugMode(true);
config.setDebugSettings(
  new DebugSettings().setAll(true));
Appending the log
config.setAppendLog(true);
config.setDebugSettings(
  new DebugSettings().setAppendLogs(true));
Setting resource timeout
config.setResourceRequestTimeout(
          Integer);
config.setResourceReadTimeout(Integer);
config.setResourceConnectTimeout(Integer);
Setting a color space
config.setDefaultColorSpace(
  ColorSpace);
config.setColorSpaceSettings(
  new ColorSpaceSettings().setTargetColorSpace(
    ColorSpace));

CSS Changes

string()

The String-function takes 2 parameters, with the second being a keyword to specify which value should be used, if there are multiple assignments for that named string on a single page.

In accordance with the CSS specifications, the identifier last-except has been renamed.

The keyword last-except

content: string(namedString, last-except);

has been replaced with first-except

content: string(namedString, first-except);

which has the same effect.

Migrating from PDFreactor 8 to PDFreactor 9

Updated default HTML styles

With PDFreactor 9 some of the standard HTML styles have been edited to be more in line with the HTML5 specification and modern browsers. While most changes are cosmetic, some will influence the layouts of existing documents. When these have a negative impact on existing documents and adapting the print styles accordingly is to complex, the following snippets can be used to undo the most significant changes:

Page Margin

The default page margins were increased from 1cm to 2cm.

@page {
    margin: 1cm;
}

Body Margin

In previous versions of PDFreactor the default margins of body elements were 8px, in accordance to the HTML specification. As these default margins are only useful in non-paged environments they were dropped in PDFreactor 9.

body {
    margin: 8px;
}

For this to take effect in multi-page documents the following snippet has to be applied as well.

Box Decoration Break

The default value of the property box-decoration-break is now slice, as per the specification. The margin, padding and border-width values around page break will be treated as 0.

* {
    box-decoration-break: clone;
}

Rounding Mode

The default value of the proprietary property -ro-rounding-mode is now floor. This avoids rare cases where accumulated rounding imprecisions could lead to unexpected layout results, but may lead to different line and page-breaks.

html {
    -ro-rounding-mode: round;
}

First Page Side

The default side of first pages has changed from left to recto (i.e. right, unless the document direction is right-to-left), as per the specification.

@-ro-preferences {
    first-page-side: left;
}

Margins after Breaks

The top margins of blocks at the start of pages and columns are no longer set to 0 for first pages and columns or after forced breaks. This matches the behavior of browsers and the requirements of the latest CSS specifications.

html {
    -ro-truncate-margin-after-break: always;
}

However, in most cases it is advisable to apply non-proprietary styles, that result in the same improvement in browsers.

h1 {
    break-before: page;
    margin-top: 0;
}

div.multiColumn > *:first-child {
    margin-top: 0;
}

API Changes

The package of the ExceedingContent class has been changed from

com.realobjects.pdfreactor.exceedingcontent

to

com.realobjects.pdfreactor.contentobserver

Deprecated API

Java

The method setDocument(Object) is obsolete, use setDocument(String), setDocument(byte[]) or setDocument(InputSource) instead. This only affects the non-client Java library.

Java and Java Client

Constructors of inner Configuration classes (except for the KeyValuePair class) that take multiple arguments have been deprecated and will be removed in a future version. Use the no-argument constructor in conjunction with individual setters instead. An exception is the KeyValuePair class whose multi-argument constructor is not deprecated. Setters now return an instance of the class, thus allowing you to chain multiple subsequent setter calls. The following example demonstrates using no-argument constructors and chainable setters by adding a user script and a user style sheet to a Configuration instance:

Old API

config.setAttachments(new Attachment(null, "http://myAttachment.zip", null, null));
config.setUserStyleSheets(new Resource("p { color: red; }", null));

New API

config
    .setAttachments(new Attachment().setUri("http://myAttachment.zip"));
    .setUserStyleSheets(new Resource().setContent("p { color: red; }"));

.NET Client

Multi-argument constructors are now obsolete in favor of object initializers.

Old API

config.Attachments.Add(new Attachment(null, "http://myAttachment.zip", null, null));
config.UserStyleSheets.Add(new Resource("p { color: red; }", null));

New API

config.Attachments.Add(new Attachment { Uri = "http://myAttachment.zip" });
config.UserStyleSheets.Add(new Resource { Content = "p { color: red; }" });

PHP Client

The PDFreactor PHP classes are now located in the namespace com\realobjects\pdfreactor\webservice\client\. To adjust your integration, just add appropriate use directives like this:

use com\realobjects\pdfreactor\webservice\client\PDFreactor as PDFreactor;
use com\realobjects\pdfreactor\webservice\client\LogLevel as LogLevel;
use com\realobjects\pdfreactor\webservice\client\ViewerPreferences as ViewerPreferences;
...

All APIs

The configuration properties mergeByteArray, mergeByteArrays, mergeURL and mergeURLs are now obsolete. Use mergeDocuments instead. Note that mergeDocuments takes one or more Resource objects, so you have to use the appropriate properties of that object, either data for binary data or uri for URLs. Java example:

Old API

// merge single document from binary source
config.setMergeByteArray(byteArray1);
// merge multiple documents from binary source
config.setMergeByteArrays(byteArray1, byteArray2);
// merge single document from URL source
config.setMergeByteURL(url1);
// merge multiple documents from URL source
config.setMergeByteURLs(url1, url2);

New API

// merge single document from binary source
config.setMergeDocuments(new Resource().setData(byteArray1));
// merge multiple documents from binary source
config.setMergeDocuments(new Resource().setData(byteArray1),
        new Resource().setData(binary2));
// merge single document from URL source
config.setMergeDocuments(new Resource().setUri(url1));
// merge multiple documents from URL source
config.setMergeDocuments(new Resource().setUri(url1),
        new Resource().setUri(url2)));

The ScriptResource type is now deprecated, use Resource instead. This change only affects Java and .NET APIs.

Migrating from PDFreactor 8.0 to PDFreactor 8.1

Java and .NET Client

When using either the Java or .NET Clients, your integration has to be adjusted. These clients are now based on the REST API rather than the SOAP API. This was done to provide an API that is more in line with the other clients.

PDFreactor Web Service settings

When using the PDFreactor Web Service with custom settings in the "pdfreactorwebservice.vmoptions" file, you have to migrate these settings to the "start.ini" located in the "PDFreactor/jetty" directory.

If your "pdfreactorwebservice.vmoptions" looked like this

-Xmx1024m
-Djava.awt.headless=true

you have to add the lines from your "pdfreactorwebservice.vmoptions" to the end of your "start.ini" file

--exec
-Dorg.apache.cxf.Logger=org.apache.cxf.common.logging.Slf4jLogger
-Dcom.realobjects.interceptConsoleOutput=true
# old pdfreactorwebservice.vmoptions settings
-Djava.awt.headless=true
-Xmx1024m

Migrating from PDFreactor 7- to PDFreactor 8+

With PDFreactor 8 we are introducing the first major API change since PDFreactor 2. One major benefit of this change is that the new Java API is identical (with a few additions) to the newly introduced Java web service client API.

Using the Legacy Java API

Java integrators can still use the old API, however we highly recommend to migrate to the new API as soon as possible, since the old one will be removed in a future release of PDFreactor. To continue using the old legacy API, just change the package from

com.realobjects.pdfreactor

to

com.realobjects.pdfreactor.legacy

Migrating to the New API

The Configuration Object

The most obvious change is the introduction of the Configuration object. In previous versions of PDFreactor, you invoked all API methods on the PDFreactor instance, however this had some disadvantages like making the PDFreactor instance non-reusable. In PDFreactor 8, you just have to create one instance of PDFreactor. The instance only has a few API methods, like convert.

All settings and options are properties of the configuration and have to be set there. While most methods are the same as in previous versions of PDFreactor, some have changed. For example, instead of add-methods, getters are used to retrieve lists on which new entries can be added. Make sure to consult the API documentation.

Converting

To create a PDF or image, you now just have to call the convert method with the configuration as a single parameter, which also specifies the input document. PDFreactor automatically detects if you are converting an HTML string, a URL or binary data.

Retrieving the Result

The new convert(Configuration) method no longer returns the PDF as binary directly. It returns a Result object which not only contains the PDF as binary, but also other useful data such as the log.

There are additional convert methods, such as convert(Configuration, OutputStream) which writes the PDF directly in the specified OutputStream instead of returning it or convertAsBinary(Configuration) which returns the binary data directly instead of a Result object. Please make sure to read the API documentation and the PDFreactor manual (Chapter "Integration").

Examples

Below are simple examples in different programming languages that show how PDFreactor was used previously and how it is used now.

Java

Old API

PDFreactor pdfReactor = new PDFreactor();

// simple settings
pdfReactor.setAddBookmarks(true);
pdfReactor.setAddLinks(true);

// adding a user style sheet
pdfReactor.addUserStyleSheet("p { color: red }", null, null, null);

// create PDF and specify the document
byte[] pdf = pdfReactor.renderDocumentFromURL("https://www.realobjects.com");

New API

PDFreactor pdfReactor = new PDFreactor();
Configuration config = new Configuration();

// specify the document
config.setDocument("https://www.realobjects.com");

// simple settings
config.setAddBookmarks(true);
config.setAddLinks(true);

// adding a user style sheet
config.getUserStyleSheets().add(new Resource("p { color: red }", null));

// create PDF
Result result = pdfReactor.convert(config);
byte[] pdf = result.getDocument();

.NET

Old API

PDFreactor pdfReactor = new PDFreactor();

// simple settings
pdfReactor.SetAddBookmarks(true);
pdfReactor.SetAddLinks(true);

// adding a user style sheet
pdfReactor.AddUserStyleSheet("p { color: red }", "", "", "");

// create PDF and specify the document
byte[] pdf = pdfReactor.RenderDocumentFromURL("https://www.realobjects.com");

New API (Changed in version 8.1)

PDFreactor pdfReactor = new PDFreactor();
Configuration config = Configuration();

// specify the document
config.Document = "https://www.realobjects.com";

// simple settings
config.AddBookmarks = true;
config.AddLinks = true;

// adding a user style sheet
config.UserStyleSheets = new List<Resource> {new Resource("p { color: red }", "")};

// create PDF
Result result = pdfReactor.Convert(config);
byte[] pdf = result.Document;

PHP

Old API

$pdfReactor = new PDFreactor();

// simple settings
$pdfReactor->setAddBookmarks(true);
$pdfReactor->setAddLinks(true);

// adding a user style sheet
$pdfReactor->addUserStyleSheet("p { color: red }", "", "", "");

// create PDF and specify the document
$result = $pdfReactor->renderDocumentFromURL("https://www.realobjects.com");

New API

$pdfReactor = new PDFreactor();
$config = array(
    // specify the document
    "document" => "https://www.realobjects.com",
    
    // simple settings
    "addBookmarks" => true,
    "addLinks" => true,
    
    // adding a user style sheet
    "userStyleSheets" => array(
        array(
            "content"=> "p { color: red }"
        )
    )
);

// create PDF
// ...as base64 encoded String
$result = $pdfReactor->convert($config);
$pdf = $result->document;

// ...as binary
$pdf = $pdfReactor->convertAsBinary($config);

To convert the base64 encoded document into binary data, you can do the following:

echo base64_decode($result->document);

Python

Old API

pdfReactor = PDFreactor()

# simple settings
pdfReactor.setAddBookmarks(True)
pdfReactor.setAddLinks(True)

# adding a user style sheet
pdfReactor.addUserStyleSheet("p { color: red }", "", "", "")

# create PDF and specify the document
result = pdfReactor.renderDocumentFromURL("https://www.realobjects.com");

New API

pdfReactor = PDFreactor()
config = {
    # specify the document
    'document': "https://www.realobjects.com",
    
    # simple settings
    'addBookmarks': True,
    'addLinks': True,
    
    # adding a user style sheet
    'userStyleSheets': [
        {
            'content': "p { color: red }"
        }
    ]
}

# create PDF
# ...as base64 encoded String
result = pdfReactor.convert(config)
pdf = result['document']

# ...as binary
pdf = pdfReactor.convertAsBinary(config)

To convert the base64 encoded document into binary data, you can do the following:

import base64
print(base64.b64decode(result['document']))

Ruby

Old API

pdfReactor = PDFreactor.new();

# simple settings
pdfReactor.setAddBookmarks(true)
pdfReactor.setAddLinks(true)

# adding a user style sheet
pdfReactor.addUserStyleSheet("p { color: red }", "", "", "")

# create PDF and specify the document
result = pdfReactor.renderDocumentFromURL("https://www.realobjects.com")

New API

pdfReactor = PDFreactor.new()
config = {
    # specify the document
    document: "https://www.realobjects.com",
    
    # simple settings
    addBookmarks: true,
    addLinks: true,
    
    # adding a user style sheet
    userStyleSheets: [
        {
            content: "p { color: red }"
        }
    ]
}

# create PDF
# ...as base64 encoded String
result = pdfReactor.convert(config)
pdf = result["document"]

# ...as binary
pdf = pdfReactor.convertAsBinary(config)

To convert the base64 encoded document into binary data, you can do the following:

require "base64"
print Base64.decode64(result["document"])

Perl

Old API

my $pdfReactor = PDFreactor -> new();

# simple settings
$pdfReactor -> setAddBookmarks('true');
$pdfReactor -> setAddLinks('true');

# adding a user style sheet
$pdfReactor -> addUserStyleSheet("p { color: red }", "", "", "");

# create PDF and specify the document
$result = pdfReactor -> renderDocumentFromURL("https://www.realobjects.com");

New API

my $pdfReactor = PDFreactor -> new();
$config = {
    # specify the document
    'document' => "https://www.realobjects.com",
    
    # simple settings
    'addBookmarks' => 'true',
    'addLinks' => 'true',
    
    # adding a user style sheet
    'userStyleSheets' => [
        {
            'content' => "p { color: red }"
        }
    ]
};

# create PDF
# ...as base64 encoded String
$result = $pdfReactor -> convert($config);
$pdf = $result->{'document'};

# ...as binary
$pdf = $pdfReactor -> convertAsBinary($config);

To convert the base64 encoded document into binary data, you can do the following:

use MIME::Base64 ();
print MIME::Base64::decode($result->{'document'});