Output API: Composition guide
1. Introduction
Starting with version v1 of the Xribe Runtime API it is possible to perform certain post-processing operations on PDF documents, termed composition. These operations are specified in a compose script and can involve inputs provided by the client, outputs generated from a template, or results from another operation in the compose script.
This document describes how compose scripts are used and which compose operations are available.
2. Compose script
In order to perform one or more compose operations you need to provide a script that specifies which operations need to be performed on which documents and in which order. This script must be passed to the message request as a reference to an independent file.
The following message request is a minimalistic example to perform a compose operation on two statically-provided PDF documents.
It assumes the presence of two PDF documents and a compose script file in the referenced environment: PDFs/centimeter.pdf
and PDFs/inch.pdf
for the PDF documents on which the script will operate and Scripts/compose.json
containing the script that will be executed.
Tip: the file name or extension given to the compose script does not matter, but using .json
or .txt
as extension allows for convenient editing in the Resource Browser.
{
"projectID" : "b8920627-26b0-48f4-bfd1-15c22206edd0",
"environmentID" : "667dee51-1e15-487c-b7ca-f1516b81ba87",
"input" : [
{
"id" : "centimeter",
"type" : "resolve",
"ref" : "PDFs/centimeter.pdf"
},
{
"id" : "inch",
"type" : "resolve",
"ref" : "https://datacenter.example.com/Documents/PDFs/inch.pdf"
}
],
"compose" : {
"ref" : "Scripts/compose.json"
}
}
As you can see, the message request assigns an id
to each statically-provided PDF document.
Each input that is provided statically must be of type resolve
.
See the Xribe Runtime API specification for other input types.
Statically provided resources must either reference a resource by path relative to the specified project and environment, or as an absolute HTTPS URL to an accessible resource. This URL may include query parameters (e.g. to pass a security token in case the resource is not publicly accessible).
The compose script in this example performs a pdf_append operation.
{
"compose" : [
{
"operation" : "pdf_append",
"inputrefs" : [
"centimeter",
"inch"
],
"outputref" : "combined"
}
],
"primaryOutput" : [
"combined"
]
}
The compose script references inputs by ID, regardless of the type of that input: an input may be statically provided and thus of type resolve
, but it may also have been generated in a previous phase of the message request; in that case its type would be template
.
The compose script lists the operation to perform, the inputrefs on which the operation should be performed, and the ID to assign to the result of the operation: outputref.
Finally, the script defines its primaryOutput: the outputref of the resource that will be considered the result of the script.
When the message request has finished processing, the Deliverable you can expect is similar to the sample output below.
{
"id" : "7fe1402a-387a-4126-985a-166b88d9ebed",
"account" : "account",
"creator" : "account\\user",
"created" : "2021-08-10T09:36:05.705Z",
"modified" : "2021-08-10T09:36:05.705Z",
"projectID" : "b8920627-26b0-48f4-bfd1-15c22206edd0",
"environmentID" : "667dee51-1e15-487c-b7ca-f1516b81ba87",
"messageID" : "040196a0-a8b4-4c0b-95a2-a3f23523af5e",
"status" : "generated",
"history" : [
{
"severity" : "info",
"message" : "Changed status to created",
"created" : "2021-08-10T09:36:05.705Z"
},
{
"severity" : "info",
"message" : "Changed status to generated",
"created" : "2021-08-10T09:36:06.429Z"
}
],
"output": [
{
"type": "intermediate",
"url": "https://resource-app-eu1.scripturaengage.com/resources/v2/data/cloud-resources/account/project/environment/PDFs/centimeter.pdf",
"id": "centimeter"
},
{
"type": "intermediate",
"url": "https://resource-app-eu1.scripturaengage.com/resources/v2/data/cloud-resources/account/project/environment/PDFs/inch.pdf",
"id": "inch"
},
{
"type": "primary",
"url": "https://inventiveproduction-updc-composition.s3.eu-west-1.amazonaws.com/account/b8920627-26b0-48f4-bfd1-15c22206edd0/667dee51-1e15-487c-b7ca-f1516b81ba87/040196a0-a8b4-4c0b-95a2-a3f23523af5e/output/combined.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAR7NO35G5X5DAHL3Y%2F20210810%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20210810T093610Z&X-Amz-Expires=600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEKH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCWV1LXdlc3QtMSJHMEUCIQD5GDRLiw7tpfiRkk6goe7yKbNdLg%2BZutgiA11U4LWXVwIgY4G648YS2yWbAGxcIDdJZi78JlEFA5pNp7mBKP2BDA8qrAIIuv%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARADGgwxMzYxOTUwNzQ0OTEiDKEvl3ZEo0coitMtmyqAAlPHL%2FSyBiXTow8EmF%2BMM4WOGCcw2kpxRJjWjuGOmSOEQsk9OiiqMVkNg4moTuBjoRSLyuPxu%2Bn4XajIYLVheZNfV8yeD5%2Fy%2BfLGXtzHPqtQVJSnOVKlOG%2BAMfnq5I%2BHx%2B1V5%2Faa4uRdf9GeAdd7R5hpR2l%2FS8n7zaknvLNUi0I3hBwxkNg7pQUn%2BdDDmwwrErJQlpQV2T1Z1PIbdnLI%2BD3BeIxNVx8pvrRtVAaEcz9m8HmBJGJC337jwTwFRl04THoqBBaKXPrPlpFrtJbk%2FJBznUfCDTKolJ3yKtQM%2Fe8P0E1IjtBUY5HD9fm5WuHTgzaSqeMf0osIVlQJIjxafvMw7f%2FIiAY6mgEKrp%2F6KChFb19XcKY1l7KSWKRJUJnVksRW3oGCqlnsdsbnwBgoddh7ggfz1CQjTD7eKSfEslu9E7tbja4ncZAAwWSpxP4O8QwD9zlYY9gfHTeXH%2F%2Barc7NuobIhth1DhC1p8bxGIxjFu1nisltldj9%2FaW%2F%2B3IpcBuyelNhfaIAf9SEqQagb6%2BbqRvMOtAKC56bitdFI9ICilxV&X-Amz-Signature=97481e595b2453caf834b28afdf6cee3ff98d0c6d4e9127de6cf720088862a9d&X-Amz-SignedHeaders=host",
"id": "combined"
}
]
}
The Deliverable contains some general information such as the Deliverable ID (id
), the user account that performed the request (account
and creator
), some timestamps (created
and modified
), the project, environment and message ID.
The Deliverable’s status
field evolves from generating
to generated
or error
.
When the status ends up in error
state, the history items give information about what went wrong in the process.
Possible errors can be related to resources that could not be found, problems during generation of output, validation errors in the compose script, or errors that occurred during the execution of the compose script.
Finally, the output
field contains an array of all the produced output.
The output of type primary
is the result of the compose script that was requested via the primaryOutput
field in the script.
This primary output is located on an accessible location (in this example on AWS S3) using a URL that has a time-limited validity.
At the time of writing the output array also includes outputs of type intermediate
, which consist of output generated from a template, statically provided inputs, and intermediate PDF documents when the composition script contains multiple operations.
Note: there is no guarantee that intermediate outputs will continue to be provided in the future.
2.1. Formal definition
A compose script must be an object described in valid JSON syntax. It is also allowed to provide a JSONata script. Such a script will be applied to the message request and must also resolve into a valid JSON object.
Tip: use JSONata Exerciser to experiment with JSONata scripts. Place the message request body in the left (JSON) pane, write a JSONata script in the top right (JSONata) pane and check if the result of the script in the bottom right pane resolves in the intended composition script.
The rest of this document will describe the operation of the service as if plain JSON was provided.
The JSON script must have two fields: compose
and primaryOutput
.
The compose
field must be an array with zero or more objects, each defining an operation.
Operations are executed in the order in which they are specified.
Each operation object in the compose
array has three required fields: operation
, inputref(s)
and outputref
.
The operation
field contains the name of the operation to apply.
Whether or not to use inputref
or inputrefs
depends on wether the operation takes a single input or multiple inputs.
The outputref
field assigns an ID to the result of that operation.
Depending on the specific operation, more fields may be required. See Compose operations for more details.
Note: all input and output IDs in the script must be unique.
It is not allowed for operations to assign an outputref
ID that is already in use by an input
defined in the message request to the API or defined as outputref
of another operation in the script.
Also: at the moment an operation is performed, the input(s) it requires must be available (i.e. they must be provided statically in the API request, they must have been generated from a template, or they must be the result of a previous operation in the script).
Finally, the primaryOutput
field in the script is an array in which exactly one ID must be listed.
2.2. Limitations
At the moment of writing there are some technical limitations that may prevent some compose scripts from completing successfully or cause an error before the script is ran.
The number of compose operations in a single compose script is limited to 100. In the unlikely event that you need to perform more than 100 operations, you need to split up the script into multiple scripts and perform them sequentially. When specifying more than 100 operations, the validation will fail before the script is started.
Currently, only one primary output is accepted for a compose script. When specifying more than one primary output, the validation will fail before the script is started.
The amount of storage available to the execution environment that runs the script is limited. Since storage use depends on the number and type of inputs and operations and the size of (intermediate or final) outputs, the amount of storage required cannot be calculated in advance. When too much storage space is used when executing the script, it will fail during its run. As a guideline it is recommended to keep the total sum size of all inputs below 200 MiB.
The script’s running time is limited to 15 minutes. This time includes fetching inputs from remote locations. When providing static input documents from a custom location (e.g. as an HTTPS URL to a document in your own data center), it is recommended to ensure that such location provides fast access.
The amount of memory available to the execution environment that runs the script is limited. Memory usage depends on the size and complexity of the input documents and images and on the resulting (intermediate or final) documents. When more memory is needed than is available, the script will fail during its run, but may not produce a clear error message. Such an out-of-memory situation has not yet been seen to occur with inputs less than 200 MiB in size.
Encrypted or password-protected PDF documents cannot be used in operations. When referencing a password-protected document in an operation, the script will fail during its run.
3. Compose operations
Below are the descriptions of the supported compose operations and their arguments. Required arguments are indicated with an asterisk. Optional arguments all have a default value that will be used when such argument is not specified in the script
3.2. Overlay
The overlay operation places an image or a PDF page on specific pages of a base document, which must be a PDF document. You can specify the location and size of the overlay and whether it should be placed on the foreground or in the background. A background overlay could, for example, be used as a watermark on a page. You can also specify on which page(s) the overlay must be applied.
3.2.1. Supported fields
operation *
|
|
inputref *
|
The ID of the base document on which to apply the overlay. |
overlayref *
|
The ID of an image in a supported format (JPEG, BMP, PNG, single-page TIFF) or of a single-paged PDF document. |
outputref *
|
An ID to assign to the result of the operation. |
x
|
A coordinate denoting the target location for the top left corner of the overlay.
The default value for this field is |
y
|
A coordinate denoting the target location for the top left corner of the overlay.
The default value for this field is |
width
|
A length denoting the desired width of the overlay. When this field is not specified, the overlay will keep its original width. |
height
|
A length denoting the desired height of the overlay. When this field is not specified, the overlay will keep its original height. |
fit
|
An indication of how the overlay should fit inside the rectangle defined by
|
background
|
Indicates where to place the overlay.
The dault value for this field is
|
pages
|
A page identification.
The default value for this field is |
3.3. Add QR barcode
The add QR barcode operation places a QR barcode on specific pages of a base document, which must be a PDF document. You can specify the location and size of the QR code. You can also specify on which page(s) the QR code must be placed.
3.3.1. Supported fields
operation *
|
|
inputref *
|
The ID of the base document on which to place the QR code. |
data *
|
The data to be encoded in the QR code. This value must be expressed as a string. |
outputref *
|
An ID to assign to the result of the operation. |
x *
|
A coordinate denoting the target location for the top left corner of the QR code. |
y *
|
A coordinate denoting the target location for the top left corner of the QR code. |
size *
|
A length denoting the desired width and height of the QR code. |
eccLevel
|
A level of error correction to be applied to the QR code.
Valid values are |
pages
|
A page identification.
The default value for this field is |
3.4. Rescale
The rescale operation reduces the size of the content of specific pages of a base document, which must be a PDF document. Note that this operation does not reduce the size of pages as a whole: pages keep their original size, but the content is scaled down. This allows for creating blank space in which, for example, an overlay or a QR code may be placed.
3.4.1. Supported fields
operation *
|
|
inputref *
|
The ID of the base document that needs its content scaled down. |
outputref *
|
An ID to assign to the result of the operation. |
scalex
|
A decimal value between 0 and 1 denoting the scale factor to be applied to the horizontal dimension.
The default value for this field is |
scaley
|
A decimal value between 0 and 1 denoting the scale factor to be applied to the vertical dimension.
The default value for this field is |
pages
|
A page identification.
The default value for this field is |
anchor
|
A specifier that determines which part of the scaled content will remain in its original position.
The default value for this field is
|
4. Common fields
Some fields are common to more than one operation. These are described here.
4.1. Distances
Distances are used as coordinates or as length specifiers. When used as a coordinate, the distance is relative to the top left corner of a document.
A distance can be expressed as an absolute value or as a contextual value.
Absolute values consist of a nonnegative whole or decimal number, optionally followed by a unit.
Supported units are mm
(millimeter), cm
(centimeter), in
(inch), pt
(point) and px
(pixel).
When no unit is specified, the value is considered to be expressed in mm
.
When a value is expressed in pt
or px
, it is interpreted at 72 dpi; so a value of 100 pt is equal to 1.389 in or 35.278 mm.
A distance can also be expressed as a contextual value: page-width
or page-height
.
The concrete distance this resolves to depends on the page size on which the operation applies.
Example: When specifying page-width
and page-height
as values for the width
and height
arguments of the pdf_overlay
operation, the overlay will be stretched to the full page size for each page in the base document, even if not all pages in that document have the same size.
This can be useful to apply a watermark, for example: a PDF document with the word SPECIMEN could be applied as a background overlay to a variety of base documents without the need to match the page size of the watermark document with the page sizes of those base documents.
4.2. Page identification
A page identification is a comma-separted list of pages, page ranges, or shortcuts on which an operation should apply. If nothing is specified, it depends on the operation what the default behavior will be.
A page is specified as a positive whole number where the first page in a document is treated as page 1.
A page range is either a set of two positive whole numbers separated by a dash, or an open range consisting of a single positive number and a dash. For example 5 - 10 (denoting pages 5, 6, 7, 8, 9 and 10), - 3 (denoting pages 1, 2 and 3), 3 - (denoting all pages in the document, except 1 and 2).
Finally, a page can also be specified as a shortcut: first
, last
, odd
, even
and all
.
Here first
denotes page 1, last
denotes the last page in the document, however many pages might be in it; odd
denotes pages 1, 3, 5, etc; even
denotes pages 2, 4, 6, etc; and all
denotes all pages in the document.
Examples:
1 - 3, last
|
The first three pages and the last one of the document. |
first, last
|
The first page and the last page in the document. |
even, last
|
All even pages in the document and also the last page, regardless of whether the document contains an even or an odd number of pages. |
1 - 4, odd, last
|
The first four pages in the document (pages 1, 2, 3 and 4), all odd pages (pages 1, 3, 5, 7, etc) and the last page of the document. |
- 10, 5 -
|
All pages in the document (the ranges of pages from 1 to 10 and from 5 to the end of the document overlap). |
Note: a page specifier that includes more pages than are present in a base document (e.g. the range 1 - 10 applied to a base document with only three pages), or a page specifier that includes pages more than once (e.g. 1 - 5, odd, first, last, 3) has no adverse effects.
5. Example scripts
5.1. Applying a watermark
Scenario: You want to generate a sample of a document by placing the words SPECIMEN in the background.
Prerequisites: A template and datasource for the document to generate and a single-page PDF document that contains the word SPECIMEN in a shade of gray. In this sample the template is generated from Templates/invoice.sdt and receives ID basedocument. The specimen document is provided statically as PDFs/specimen.pdf and receives ID specimen.
Sample message request:
{
"projectID" : "b8920627-26b0-48f4-bfd1-15c22206edd0",
"environmentID" : "667dee51-1e15-487c-b7ca-f1516b81ba87",
"input" : [
{
"id" : "specimen",
"type" : "resolve",
"ref" : "PDFs/specimen.pdf"
},
{
"id" : "basedocument",
"type" : "template",
"ref" : "Templates/invoice.sdt",
"data" : {},
"outputParameters" : {
"type" : "pdf"
}
}
],
"compose" : {
"ref": "Scripts/apply-watermark.json"
}
}
Compose script:
{
"compose": [
{
"operation" : "pdf_overlay",
"inputref" : "basedocument",
"overlayref" : "specimen",
"outputref" : "watermarked",
"x" : 0,
"y" : 0,
"width" : "page-width",
"height" : "page-height",
"fit" : "fill",
"background" : true,
"pages" : "all"
}
],
"primaryOutput" : [
"watermarked"
]
}
Note that numbers and booleans can be specified as a string or as a native value as in the example above.
The result of the request will be available in the Deliverable output object with ID watermarked.
5.2. Concatenating documents (simple)
Scenario: A customer wants to receive his digital invoices by regular mail. To prevent having to create multiple invoice templates (one for delivery as attachment to an e-mail and one with a mail address at the correct position for a windowed envelope), we generate the invoice from the regular digital invoice template and add a cover page that contains the customer’s address at the correct position. We also add a small QR code to the cover page to steer the enveloping machine. Finally, we add a pregenerated 'terms of service' page to the document.
Prerequisites: An invoice template and datasource: Templates/invoice.sdt receives ID invoice. An addressee template and datasource: Templates/addressee-cover-page.sdt receives ID addressee. A static PDF containing the terms of service: PDFs/terms-of-service-v1.3.pdf receives ID terms.
Sample message request:
{
"projectID" : "b8920627-26b0-48f4-bfd1-15c22206edd0",
"environmentID" : "667dee51-1e15-487c-b7ca-f1516b81ba87",
"input" : [
{
"id" : "terms",
"type" : "resolve",
"ref" : "PDFs/terms-of-service-v1.3.pdf"
},
{
"id" : "addressee",
"type" : "template",
"ref" : "Templates/addressee-cover-page.sdt",
"data" : {},
"outputParameters" : {
"type" : "pdf"
}
},
{
"id" : "invoice",
"type" : "template",
"ref" : "Templates/invoice.sdt",
"data" : {},
"outputParameters" : {
"type" : "pdf"
}
}
],
"compose" : {
"ref": "Scripts/concatenate-documents.json"
}
}
Compose script:
{
"compose": [
{
"operation" : "pdf_add_qr",
"inputref" : "addressee",
"outputref" : "addressee-with-qr",
"data" : "a steering code",
"x" : "10 mm",
"y" : "25 mm",
"size" : "15 mm",
"pages" : "first"
},
{
"operation" : "pdf_append",
"inputrefs" : [
"addressee-with-qr",
"invoice",
"terms"
],
"outputref" : "mailable-invoice"
}
],
"primaryOutput" : [
"mailable-invoice"
]
}
The result of the request will be available in the Deliverable output object with ID mailable-invoice. It will be a single PDF document with the first page containing the customer’s address, the following page(s) containing the invoice information and the last page(s) containing the terms of service.
5.3. Adding a transpromo banner
Scenario: You want to add a narrow information banner to an invoice, notifying customers of a relevant promotion or offer. Since such a promotion or offer is time-bound and may be targeted to specific customer segments, you don’t want to create multiple templates or edit templates frequently. Moreover, the marketing department that creates the promotional collateral may not be in a position to alter invoice templates. In order to add transpromo banners to invoices, we take a regular invoice statement, downscale the content slightly in the vertical dimension and add the promotional banner.
Prerequisites: An invoice template and datasource: Templates/invoice.sdt receives ID invoice. A banner image supplied by the marketing department: Marketing/promo-banner-black-friday.tiff receives ID banner.
Sample message request:
{
"projectID" : "b8920627-26b0-48f4-bfd1-15c22206edd0",
"environmentID" : "667dee51-1e15-487c-b7ca-f1516b81ba87",
"input" : [
{
"id" : "banner",
"type" : "resolve",
"ref" : "Marketing/promo-banner-black-friday.tiff"
},
{
"id" : "invoice",
"type" : "template",
"ref" : "Templates/invoice.sdt",
"data" : {},
"outputParameters" : {
"type" : "pdf"
}
}
],
"compose" : {
"ref": "Scripts/apply-transpromo.json"
}
}
Compose script:
{
"compose": [
{
"operation" : "pdf_rescale",
"inputref" : "invoice",
"outputref" : "rescaled-invoice",
"scalex" : 1,
"scaley" : 0.88,
"pages" : "first",
"anchor" : "bottom"
},
{
"operation" : "pdf_overlay",
"inputref" : "rescaled-invoice",
"overlayref" : "banner",
"outputref" : "invoice-with-banner",
"x" : 0,
"y" : "5 mm",
"width" : "page-width",
"height" : "30 mm",
"fit" : "aspect-down",
"background" : false,
"pages" : "first"
}
],
"primaryOutput" : [
"invoice-with-banner"
]
}
Note: In this sample we expect that the invoice is generated in A4 size (210 x 297 mm).
We scale vertically by 0.88 with a bottom
anchor, meaning that a space of 35 mm at the top of the page is freed.
When placing the banner we leave a blank space of 5mm at the top of the page and make the banner fit in the next 30mm.
We use a fit of aspect-down
so that the banner keeps its original aspect ratio and is not scaled up (which might cause aliasing artifacts).
The result of the request will be available in the Deliverable output object with ID invoice-with-banner.
5.4. Concatenating documents (dynamic)
As mentioned in Formal definition, it is possible for a compose script to contain JSONata. One example of the advantage of using JSONata is that it can allow you to create reusable compose scripts that are not dependent on predefined input names or a fixed number of inputs.
Below is a compose script that appends all inputs specified in the API request. An example use case could be a request to generate PDF output from one or more templates and adding zero or more statically referenced PDF attachments.
{
"compose": [
{
"operation" : "pdf_append",
"inputrefs" : [
$.input.id
],
"outputref" : "merged"
}
],
"primaryOutput" : [
"merged"
]
}
This JSONata script is applied to the API request. Regardless of how many inputs it defines (generated from a template or provided statically), they will all be appended into a single PDF document that will be available in the Deliverable output object with ID merged.
Note: when using JSONata this way, the order in which inputs are defined in the request becomes important as it defines the order in which the documents will be appended.