Skip to content

Migrating from jsreport

This guide moves your jsreport templates into a Typefold workspace without rewriting them, so your application can render them by name or shortid as before.

  • Your templates use the Handlebars engine with the chrome-pdf or xlsx recipe. See which templates import.
  • You can open the workspace in Studio (any workspace role can import and publish), or you have an API key with the import scopes if you import from a script.

In jsreport Studio, export the folders and templates you want to move, together with the scripts, assets, components and data they use. You get a .jsrexport file. It may be up to 20 MB.

  1. In Studio, open Workspace → Editor. Click + above the explorer and choose Import…, or choose Import into folder… in the … menu of a folder.
  2. Drop the .jsrexport file, or click Choose files and select it. The Import jsreport export dialog lists:
    • each template it will import, with its format and the folder it lands in;
    • notes on what the import changed so the template runs here, such as a removed afterRender step, and on referenced files the export does not contain, with the path to add them at;
    • templates it cannot import, with the reason.
  3. Check Import into folder. It defaults to a folder named after the file, such as invoices for invoices.jsrexport. Use / for nested folders, or clear it to import at the workspace root.
  4. Click Import file.

The import publishes each template it writes. If one cannot be published, you see a warning with the reason, and it stays a draft until you fix it and click Publish.

  1. Read every note from step 2, and add any missing files at the paths they name.
  2. If a template signs its PDFs, a workspace owner or administrator uploads the certificate under Manage → Signing certificates with Upload certificate, using the name the note gives.
  3. Open each imported template in Studio and click Update preview with its sample data.
  4. Compare the output with jsreport’s. Chrome compatibility lists where the renderer follows Chrome’s print layout, and CSS support what it does not support.
  5. Publish any template that stayed a draft.

Your application renders imported templates by name through the Reports API. Recipe names are not needed:

Terminal window
curl $PDF_URL/v1/reports/render \
-H "x-api-key: $API_KEY" \
-H "content-type: application/json" \
--fail-with-body -o invoice.pdf \
-d '{
"format": "pdf",
"template": { "name": "Invoice" },
"data": { "number": "INV-1001" }
}'

PDF_URL and API_KEY are set as in the quickstart. The Reports API guide lists the scopes the key needs and the other request options.

  • Shortids. To render by the shortid your application stored in jsreport, send "template": { "shortid": "rkJTnK2ce" }, or use TemplateRef.ByShortid in .NET, byShortid in TypeScript and JavaScript, or by_shortid in Python.
  • Listing templates. If your application listed templates with odata/templates, call GET /v1/reports instead (ListReportsAsync, listReports or list_reports). It returns each template’s shortid too.
  • Clients. The .NET, TypeScript and JavaScript and Python clients replace jsreport’s own clients.

Once the output matches, switch your application over.

Two endpoints do what the Studio dialog does: one inspects the archive, one imports it. Both take the archive as base64 in JSON, with an optional folderPath to import into, such as Projects/Invoices. Missing folders are created. Without folderPath, the archive lands at the workspace root. Every field is in the API reference.

  1. Build the request body:

    Terminal window
    # On macOS, use: base64 -i invoices.jsrexport
    printf '{"archiveBase64":"%s","folderPath":"Invoices"}' \
    "$(base64 -w0 invoices.jsrexport)" > import.json
  2. Inspect the archive. This changes nothing:

    Terminal window
    curl $PDF_URL/v1/reports/import/inspect \
    -H "x-api-key: $API_KEY" \
    -H "content-type: application/json" \
    --data @import.json
    {
    "items": [
    {
    "name": "Invoice",
    "format": "pdf",
    "assets": 4,
    "folderPath": "Invoices",
    "changes": [
    "Script \"format-rows\": its afterRender step was removed; only beforeRender runs.",
    "File \"logo.png\" is not in the export; the template renders once the workspace has it at /Invoices/shared/logo.png."
    ]
    }
    ],
    "unsupported": [
    {
    "name": "Shipping labels",
    "recipe": "html-to-xlsx",
    "engine": "handlebars"
    }
    ]
    }

    changes holds the notes for each template, and unsupported the templates that will be skipped, with a reason when the recipe is supported but something the template uses is not.

  3. Import it:

    Terminal window
    curl $PDF_URL/v1/reports/import \
    -H "x-api-key: $API_KEY" \
    -H "content-type: application/json" \
    --data @import.json
    {
    "imported": 1,
    "updated": 0,
    "skipped": 0,
    "unpublished": [
    {
    "name": "Invoice",
    "message": "Missing workspace files: Invoices/shared/logo.png (referenced by template.html)."
    }
    ]
    }

    imported counts new templates, updated those that got a new draft, and skipped those left as they were (see import again). unpublished lists the templates that stayed drafts, with why. Fix what each message names and publish them, then carry on with step 3.

The import writes every template or none. One template that cannot be published does not stop the others.

Action Endpoint Scopes
Inspect an archive POST /v1/reports/import/inspect template:read
Import an archive POST /v1/reports/import template:write; asset:manage to upload fonts, images and workbooks; template:publish to publish the imported templates

Without asset:manage, the import leaves out fonts, images and workbooks, and templates that use them report the files as missing. Without template:publish, every template stays a draft and is listed in unpublished. See API keys and scopes.

You can import the same or an updated export again. IDs stay stable, so applications that render by name, ID or shortid keep working.

  • A template nobody has edited since its import gets a new draft and is published again. One imported from the identical archive is skipped.
  • A template someone has edited in Typefold since its import is skipped, so your changes are never overwritten.
  • A template you deleted comes back.
  • A workspace file whose content changed is saved as a new revision. A global or folder script that is already in the workspace stays as it is.
jsreport recipe Becomes
chrome-pdf A PDF template, with its Chrome print settings in page.json.
xlsx An XLSX template that fills the workbook the jsreport template used.

Both need the Handlebars engine. Templates with other recipes or engines are listed as unsupported and skipped; the rest of the archive still imports. A Handlebars chrome-pdf or xlsx template is also skipped when:

  • a script it uses does not parse, or is larger than 100 KB;
  • it would run more than 10 scripts, counting global and folder scripts;
  • its page size or margins cannot be mapped, such as format A6 or a margin in pt;
  • its name is longer than 120 characters.

The whole archive is refused when it is larger than 20 MB, is not a valid zip, has an entry that is missing required fields, or has two templates with the same name and format. The other import limits are in limits.

jsreport folders become workspace folders under the folder you import into, and every file keeps its folder.

In jsreport In Typefold
Folder Workspace folder
Handlebars chrome-pdf or xlsx template Template, with a stable ID, that keeps its jsreport shortid
Template content The template’s HTML source
Template helpers helpers.js
Chrome headerTemplate and footerTemplate, with displayHeaderFooter header.html and footer.html
Chrome print settings, PDF settings, language page.json (see page settings)
Component Partial, rendered by {{component "..."}} as before
HTML, .xml and .txt assets Partials
.js assets Scripts
.css assets Stylesheets
.json assets Localizations (translations)
.ttf, .otf, .woff2, .png, .jpg, .jpeg, .svg and .xlsx assets Uploaded fonts, images and workbooks (needs asset:manage)
Data item Sample data file, used for previews
Global script Script that runs before every render
Folder script Script that runs before renders in that folder and below
Script assigned to a template Script beside the template, which the template runs by name

Blank header and footer templates are left out, and Chrome’s default date, title and URL bands are not drawn. Assets of other types are not imported. Data items become sample data for previews only; your application still sends the live data with every render request.

Paths keep working:

  • Root paths such as {#asset /shared/logo.png @encoding=dataURI} and {{component "/shared/address"}} are rewritten to start at the folder you imported into.
  • jsreport finds a file named without a folder anywhere in the export. When exactly one file has that name, the import rewrites the reference to that file’s path from the template’s folder.

PDF templates also get body { font-family: Arial, sans-serif; } ahead of their own styles, because jsreport’s Chrome prints in Arial unless told otherwise. Your own CSS still wins.

A chrome-pdf template’s settings become its page.json:

jsreport setting page.json
format (A3, A4, A5, Letter, Legal) Page size. Any other format makes the template unsupported.
width and height Custom page size, when no format is set.
Neither Letter, as in jsreport.
landscape Orientation.
marginTop, marginRight, marginBottom, marginLeft Margins in millimetres. Lengths in px, mm, cm or in; a bare number is pixels. An unset side is 0.
pageRanges Page ranges, when they are valid.
preferCSSPageSize preferCssPageSize, so an @page size prints on its own paper as in jsreport.
pdfMeta title, author, subject, creator, producer, language, keywords PDF document properties. Keywords are split at commas.
pdfPassword with a password or owner password Passwords and permissions. As in jsreport, permissions it does not name are not granted.
pdfSign A signature with its reason. The certificate is named after the jsreport asset without its extension: Company seal.p12 becomes Company seal.
pdf-utils operations operations, naming the other template. Disabled operations are skipped (see what changes).
pdfAccessibility with PDF/UA on PDF/UA-1, if the template has a heading (see what changes).
Template localization.language Locale. Without one, en-US.

Templates that rely on these need no changes:

  • Paths. Partials, {#asset} paths and {{component}} calls resolve as in jsreport. See how paths resolve.
  • Components. {{component "path"}} renders a partial the way jsreport components do. See reuse a block with component.
  • Helpers and scripts. Helper files and beforeRender scripts take the same forms as in jsreport. require("moment") offers a subset of Moment for formatting dates. require("bwip-js") offers only the synchronous toSVG, so a barcode helper that used toBuffer must switch to toSVG or to the built-in qrCode and barcode helpers.
  • Localization. In scripts, require("jsreport-proxy") offers localization.localize(key), which returns the label for the request’s language, or the key when the translation file has none.
  • pdf-utils. pdfCreatePagesGroup, pdfAddPageItem, pdfDest and pdfFormField work as in jsreport. Other templates read $pdf, also once per page with renderForEveryPage. See page data and form fields.
  • @page rules. Margins and orientation from a template’s @page rule apply as Chrome printed them, and its size too when the template had preferCSSPageSize.
  • Header and footer classes. The pageNumber and totalPages classes from Chrome’s print templates work unchanged in header.html and footer.html. See Lay out pages.
  • Fonts. The name in font-family need not match the name inside the font file, so font-family: Regular keeps working, and fonts embedded as data URLs work too. Arial has the same metrics as in jsreport’s Chrome on Linux, so lines and pages break in the same places.
  • Layout. Full-page sections built with html, body { height: 100% } keep one section per page, as in Chrome.
  • Workbooks. Imported xlsx templates become workbook templates, and the row commands of jsreport’s xlsx recipe work the same.
  • Inline Handlebars. Text that jsreport rendered with the html recipe can go through string rendering with "engine": "handlebars".

The import adjusts these so templates run here. Each appears as a note in the import dialog and in the inspect response.

  • afterRender steps are removed. A script with only afterRender is left out; one with both keeps its beforeRender. Do any post-processing in your application after rendering.
  • pdf-utils append, prepend and merge operations become pages from other templates, naming the other template. An operation is left out, with a note, when its template is not a chrome-pdf template in the same archive, or beyond the tenth.
  • Signing certificates are not imported. A template with pdfSign fails to render until its .p12 or .pfx file is uploaded with the name the note gives (see step 3).
  • PDF/UA needs a heading. PDF/UA output here fails without an h1 to h6 heading, which jsreport never required. If neither the template nor the partials it includes has one, PDF/UA is turned off with a note. Add a heading and import again, or ask for PDF/UA per request with pdf.pdfua.
jsreport feature What to do instead
Recipes other than chrome-pdf and xlsx, engines other than Handlebars Rebuild the template in Studio.
Other jsreport-proxy calls, such as render, documentStore, folders and assets Send all the data the template needs with the render request.
Chrome scale, viewport size, print background and wait-for-JS No equivalent. Check the layout in a preview.
pdf-utils PDF/A setting Turn on PDF/A for the template.
pdfFormField in a PDF/A template Turn PDF/A off for that template, or leave the fields out.
$pdf read in a script Read $pdf in the template or its helpers.

A script that makes any other jsreport-proxy call fails with an error naming the call. Scripts have no network, file system or Node.js APIs; see template language. Not supported lists the general gaps and their workarounds.