L'API qui dessine vos documents

Votre programme décrit en JSON le document qu'il veut, l'envoie en un appel, et le PDF fini revient dans la réponse. Cette page dit comment, en mots simples d'abord, puis pour le développeur qui écrit cet appel, puis clé par clé.

En mots de tous les jours

Écrit pour tout le monde. Vous pouvez vous arrêter à la fin et savoir tout de même ce que c'est et à quoi cela sert.

Pensez à commander un gâteau. Vous écrivez sur un bout de papier ce que vous voulez dessus, vous tendez le papier par-dessus le comptoir, et le gâteau revient par le même comptoir un moment plus tard. Rien d'autre ne se passe, et vous ne voyez jamais la cuisine.

Le bout de papier, ici, est un texte que votre programme écrit. Il dit ce qui va sur la page : cette phrase à cet endroit, ce tableau en dessous, ce logo dans le coin, ce code carré en bas. Tout ce dont la page a besoin et que le texte ne peut pas porter — une photographie, un papier à en-tête — voyage à côté.

Ce qui revient est le document fini, prêt à être envoyé à un client ou à une imprimerie. Il revient dans le même souffle, pas dans une heure et pas dans une boîte aux lettres.

Il y a vingt et une choses que l'on peut poser sur une page : des paragraphes, des tableaux, des photographies, des pages prises dans un autre document, les cinq sortes de codes carrés et rayés, des formes simples, les cases d'un formulaire à remplir, et des liens cliquables. Chacune a ici une page à elle, avec le plus court exemple qui la dessine.

Si vous lisez ceci pour décider si cela convient, la réponse est dans cette liste : tout ce que l'on peut demander y est écrit, et rien d'autre n'est nécessaire pour commencer.

Pour le développeur qui écrit l'appel

Écrit pour celui qui écrit le code appelant : le transport, le modèle, le repère de coordonnées et les modes d'échec.

Le transport

Un seul point d'entrée, une seule méthode : un POST avec un corps multipart/form-data. La partie nommée json_data porte toute la description en JSON. Toute autre partie est un binaire nommé que la description désigne par le nom de cette partie — un PDF employé comme gabarit ou importé page par page, une image, un profil ICC, un paquet XMP, le XML d'une facture. Il n'y a pas d'étape d'envoi préalable ni d'identifiant de ressource à garder : une partie vit le temps de l'appel où elle a voyagé.

L'authentification est une clé porteuse vérifiée par le proxy inverse, qui la résout en un nom de client et transmet ce nom dans un en-tête à lui. Un succès répond 200 avec application/pdf, plus le temps de lecture et le temps de dessin en millisecondes. Un refus répond un code de statut et une phrase en texte brut. Il n'y a pas d'enveloppe à ouvrir et pas de travail à surveiller.

De quoi un appel et sa réponse sont faits
L'appel qui dessine un document POST /render
L'appel qui dit que le service est debout GET /health
La forme dans laquelle le corps est envoyé multipart/form-data
La partie qui porte la description json_data
Toute autre partie un binaire nommé que la description désigne
Comment la clé voyage Authorization: Bearer <key>
Ce que le proxy transmet au moteur de rendu x-hqf-client: <name>
Ce que porte un succès 200 application/pdf
Le temps passé à lire la description x-hqf-parse-ms
Durée du dessin x-hqf-render-ms
Ce que porte une erreur text/plain

Le modèle

Une requête est un document plat : une liste d'articles, et à côté les ressources que ces articles nomment. Polices, espaces de couleur, dégradés, états graphiques, calques et dessins sont chacun déclarés une fois sous un nom, et un article renvoie à ce nom. Rien n'est positionnel et rien n'est implicite — un nom qu'un article appelle et qu'aucune déclaration ne porte est refusé, le nom figurant dans le message.

Les pages sont créées par ce qu'on dessine dessus, elles ne sont pas déclarées d'avance. Un article dit sur quelles pages il se pose ; le défaut, un tableau vide, veut dire toutes les pages, et c'est ainsi qu'un pied de page ou un filigrane s'écrit une seule fois. Un tableau qui déborde de son cadre se poursuit dans le cadre suivant et crée les pages qu'il lui faut, et les articles posés sur toutes les pages l'y suivent.

La géométrie est en points typographiques avec l'origine en bas à gauche, comme le veut le format lui-même. Toute coordonnée peut être une expression arithmétique entre guillemets portant sur les dimensions de la page et le numéro de page courant, si bien qu'une marge de droite s'écrit "{page_width} - 56" plutôt qu'un nombre que votre programme aurait dû calculer. Un article peut se nommer lui-même et un autre s'accrocher à son bord, ce qui garde une mise en page juste quand la hauteur d'un bloc n'est connue qu'une fois composé.

Ce qu'une requête porte à côté du dessin

La même requête énonce les métadonnées du document, la norme d'archivage qu'il revendique, l'intention de sortie contre laquelle ses couleurs sont lues, la compression de ses flux, le sommaire et les destinations nommées, les fils d'articles, les étiquettes de page, les préférences de lecture qu'une visionneuse respecte, et le XML de facture que porte un document Factur-X. Un seul appel produit un fichier fini qui revendique ses normes : il n'y a pas de seconde passe ni d'outil de retouche.

Les modes d'échec, et ce qu'ils coûtent

La validation est complète et a lieu avant que rien ne soit dessiné : le serveur décline une requête plutôt que de produire un document qui aurait perdu quelque chose en silence. Un refus est une phrase qui nomme la clé ou le nom en cause, et elle mérite d'être journalisée mot pour mot.

Les clés inconnues sont ignorées, ce qui rend une requête écrite pour une version plus récente sans danger à envoyer à une plus ancienne. Une clé écrite deux fois dans le même objet est déclinée par son nom. Les combinaisons qui se contrediraient — une matrice avec une rotation, un lien portant à la fois une cible courte et une action complète, une cellule de tableau portant deux choses — sont déclinées par leur nom également.

Toutes les réponses du serveur, par leur numéro
200 Le PDF est le corps, et les deux en-têtes de durée se tiennent à côté.
400 La description a nommé quelque chose que le dessin n'a pas pu employer : une police qu'aucune déclaration ne porte, une coordonnée illisible, un gabarit qui n'est pas un PDF, un lien vers une page au-delà de la dernière. Le corps est une phrase qui dit laquelle.
402 Quelque chose que couvre une clé de licence. Le corps nomme ce qui a été demandé, et une clé l'active.
413 Le corps, ou le document qu'il produirait, dépasse un plafond réglé sur le service. Le corps nomme ce plafond.
500 Un échec du côté du service plutôt que dans la requête.

La référence : chaque clé d'une requête

Le format lui-même. Chaque chiffre a été relevé sur les sources du serveur de rendu et vaut pour la version en production.

La plus petite requête qui dessine quelque chose

Une police déclarée, un article, une ligne de texte. Enregistrez-le, envoyez-le, et un PDF d'une page revient.

{
  "standard_fonts": [{ "name": "sans", "face": "helvetica" }],
  "items": [
    {
      "type": "text",
      "rect": { "llx": 56, "lly": 700, "urx": "{page_width} - 56", "ury": 780 },
      "content": ["Invoice 2026-014"],
      "font": "sans",
      "font_size": 24
    }
  ]
}

Le même appel, écrit trois fois

Copiez celui des trois qui correspond à vos outils. Chacun envoie la description et un fichier qu'elle emploie, et écrit la réponse directement sur le disque.

Depuis un terminal

Une partie porte la description, une partie par fichier qu'elle emploie. Le nom d'une partie est le nom sous lequel la description appelle ce fichier.

curl -X POST https://your-server/render \
  -H "Authorization: Bearer $HQF_PDF_KEY" \
  -F "json_data=@invoice.json;type=application/json" \
  -F "letterhead.pdf=@letterhead.pdf" \
  -o invoice.pdf

Depuis Python

La réponse est le PDF lui-même, écrivez donc le corps directement dans un fichier. Les deux en-têtes de durée sont là chaque fois que le dessin a réussi.

import json
import pathlib
import requests

LETTERHEAD = pathlib.Path("letterhead.pdf")

description = {
    "standard_fonts": [{"name": "sans", "face": "helvetica"}],
    "items": [
        {
            "type": "text",
            "rect": {"llx": 56, "lly": 700, "urx": "{page_width} - 56", "ury": 780},
            "content": ["Invoice 2026-014"],
            "font": "sans",
            "font_size": 24,
        }
    ],
}

answer = requests.post(
    "https://your-server/render",
    headers={"Authorization": f"Bearer {KEY}"},
    files={
        "json_data": ("request.json", json.dumps(description), "application/json"),
        "letterhead.pdf": ("letterhead.pdf", LETTERHEAD.read_bytes()),
    },
    timeout=120,
)
answer.raise_for_status()
pathlib.Path("invoice.pdf").write_bytes(answer.content)
print(answer.headers["x-hqf-render-ms"], "ms drawing")

Depuis JavaScript

Un corps de formulaire et l'appel réseau standard, si bien que les mêmes lignes tournent dans un navigateur et côté serveur. Un refus arrive sous forme d'une phrase en texte brut.

const description = {
  standard_fonts: [{ name: "sans", face: "helvetica" }],
  items: [
    {
      type: "text",
      rect: { llx: 56, lly: 700, urx: "{page_width} - 56", ury: 780 },
      content: ["Invoice 2026-014"],
      font: "sans",
      font_size: 24,
    },
  ],
};

const body = new FormData();
const asJson = JSON.stringify(description);
body.append("json_data", new Blob([asJson], { type: "application/json" }));
body.append("letterhead.pdf", letterheadBlob, "letterhead.pdf");

const answer = await fetch("https://your-server/render", {
  method: "POST",
  headers: { Authorization: `Bearer ${key}` },
  body,
});
if (!answer.ok) {
  throw new Error(await answer.text());
}
const pdf = new Uint8Array(await answer.arrayBuffer());

Les clés d'une requête

Trente-trois clés se tiennent au premier niveau d'une requête, et une seule est obligatoire. Toute autre clé vaut par défaut la valeur de la colonne d'à côté, si bien qu'une requête énonce ce dont elle a besoin et rien d'autre.

Chaque clé qu'une requête porte à son premier niveau
Clé Type Défaut À quoi ça sert
items tableau obligatoire Tout ce qui est dessiné sur les pages, dans l'ordre où c'est dessiné. C'est la seule clé qu'une requête ne peut pas omettre.
page objet 595 × 842 La feuille sur laquelle chaque page est coupée, en points, sauf si une page nomme la sienne.
page_sizes tableau [] Une feuille à elle pour une page numérotée.
page_boxes tableau [] Les boîtes de rognage, de fond perdu, de coupe et de dessin d'une page, ce qu'une imprimerie lit avant tout le reste.
page_turns tableau [] Dans quel sens une page est présentée à celui qui ouvre le fichier.
page_tab_orders tableau [] L'ordre dans lequel le clavier parcourt les champs d'une page.
fonts tableau [] Les polices portées dans la requête en base64, chacune sous un nom que les articles appellent.
type1_fonts tableau [] Les polices de type 1, portées de la même façon.
standard_fonts tableau [] L'une des quatorze polices que tout lecteur porte déjà, sous un nom de votre choix.
font_variants tableau [] Une police déjà déclarée, avec les ligatures, les petites capitales, les chiffres elzéviriens ou le crénage activés.
type3_fonts tableau [] Une police dont les lettres sont des dessins plutôt que des contours.
font_chains tableau [] Deux polices ou plus essayées dans l'ordre, si bien qu'une lettre qui manque à la première est prise à la suivante.
templates objet Les PDF fournis posés sous la première page, sous les pages du milieu et sous la dernière.
renumber tableau [] Où la numérotation des pages repart, dans quel style, derrière quel préfixe.
bookmarks tableau [] Le sommaire que le lecteur ouvre à côté de la page, imbriqué aussi profondément que vous voulez.
destinations tableau [] Des endroits nommés du document qu'un lien ou une action peut viser.
articles tableau [] Des fils de lecture qui portent le lecteur d'une colonne à la suivante.
document_parts objet L'arbre des parties qu'un document PDF 2.0 déclare, chacune revendiquant une suite de pages et portant des valeurs nommées.
metadata objet Titre, auteur, sujet, producteur, date de création, état de recouvrement, et schémas de votre cru.
archive chaîne La norme d'archivage que le fichier revendique : `pdfa3b` ou `pdfa4`.
invoice objet Le XML de facture porté à l'intérieur du document, avec son profil, sa version, sa relation et sa date.
reading objet Comment on demande au lecteur d'ouvrir le fichier : quel panneau, quelle disposition, quelle page, quel agrandissement, et quoi envoyer à l'imprimante.
base_uri chaîne L'adresse contre laquelle un lien relatif est résolu.
version chaîne celle du moteur La version de PDF que le fichier déclare : de `1.4` à `1.7`, ou `2.0`.
compression objet Si les flux sont compressés, à quel niveau, et si les petits objets voyagent groupés.
output_intent objet sRGB Le profil de couleur contre lequel les couleurs du fichier doivent être lues.
images tableau [] Des métadonnées attachées à une image fournie, énoncées ou portées en paquet.
color_spaces tableau [] Séparations, palettes indexées, Lab, gris et RVB calibrés, et espaces fondés sur un profil, chacun sous un nom qu'une couleur peut appeler.
shadings tableau [] Les dégradés axiaux et radiaux, chacun sous un nom qu'un remplissage peut appeler.
graphics_states tableau [] Des états nommés portant une opacité de remplissage, une opacité de trait et un mode de fusion.
layers tableau [] Des calques nommés que le lecteur peut afficher ou masquer, et qui disent s'ils s'impriment.
drawings tableau [] Des dessins vectoriels nommés, c'est-à-dire ce qu'un bouton poussoir montre comme icône.
transparency objet L'espace de fusion dans lequel travaille le groupe de la page, et s'il est isolé ou en défonce.

Les vingt et une choses qu'un article peut être

Un article est un objet qui porte un type. Chaque type a une page à lui : ce que c'est, ce qu'un développeur doit en savoir, chaque clé qu'il accepte avec son défaut, et une requête entière qui le montre.

Texte

Les tableaux

Images et pages importées

Les codes-barres et les codes carrés

Formes

Champs de formulaire

Navigation

Les formes dont chaque clé est faite

Une poignée d'objets reviennent partout dans le format. Ils sont écrits une fois ici, et chaque page qui en emploie un le nomme avec ces mots.

Les objets dont les clés sont faites
rect Quatre coordonnées — `llx`, `lly`, `urx`, `ury`. L'origine est en bas à gauche de la feuille et l'axe vertical monte, ce qui est la convention du format lui-même.
coordonnée Un nombre, ou une expression entre guillemets sur `+ - * / ( )` et les variables `{page_width}`, `{page_height}`, `{current_page}`, `{total_pages}`, `{llx}`, `{lly}`, `{urx}`, `{ury}`. Une expression s'imbrique jusqu'à cent niveaux, ce qui permet à un article de se caler sur la largeur de la page sans que l'appelant calcule quoi que ce soit.
color Un nombre pour un gris, trois pour le rouge, le vert et le bleu, quatre pour les quatre encres d'imprimerie, tous de 0 à 1. Aussi `{"gray": n}`, et `{"space": "name", "components": …}` pour un espace que la requête a déclaré.
stroke Une `width`, et facultativement une `color`, un motif `dash` avec sa phase, un `cap` et un `join`.
pages Les pages, numérotées à partir de un, sur lesquelles un article est dessiné. Le défaut, un tableau vide, le dessine sur toutes les pages, et c'est ainsi qu'un pied de page s'écrit une seule fois.
layer Le calque déclaré auquel appartient un article, c'est-à-dire ce que le lecteur affiche ou masque.
id / relative_to Un article se nomme lui-même avec `id` ; un autre se place contre le bord `top` ou `bottom` de celui-là avec un `offset`, si bien qu'un paragraphe suit un tableau dont personne ne connaissait la hauteur d'avance.
transform / rotate Six nombres pour une matrice, ou un nombre de degrés dans le sens trigonométrique autour du centre de la boîte. Un article dit l'un ou l'autre.

Comment une requête est lue

  • Une clé que le serveur ne connaît pas est ignorée, si bien qu'une requête écrite pour une version plus récente se dessine sur une plus ancienne.
  • Une clé écrite deux fois dans le même objet est refusée, et le message nomme la clé.
  • Un choix sans charge s'écrit en chaîne nue — "center", "pdfa4". Un choix qui porte une charge est un objet à une seule clé, qui nomme la variante — {"points": 12}, {"page": 3}.
  • Une clé facultative absente et une clé explicitement à null se lisent de la même façon, ce qui laisse un sérialiseur écrire l'une ou l'autre.
  • La même description, envoyée deux fois, rend deux fois les mêmes octets : l'identifiant du fichier est une empreinte de ce qui a été écrit, si bien que deux exécutions se comparent comme des fichiers.

Les chiffres auxquels le service se tient

Les chiffres auxquels le service se tient
Le corps d'une requête 512 MiB
Profondeur d'imbrication d'une expression 100
Entrées d'une palette indexée 256
Niveau de compression 1 – 9
Correction d'erreur d'un code Aztec 0 – 90 %
Colonnes d'un code PDF417 1 – 30
Niveau de correction d'un code PDF417 0 – 8
Polices dans une chaîne de polices 2 ou plus
Pages du gabarit du milieu 1
Caractères du nom d'une police gardée 64
Pages d'un document ce que porte votre forfait

Où aller ensuite

Comment un programme demande ses pages Tout ce que la bibliothèque met sur une page Tout ce que le service garde, en entier

Voir les prix Voir les exemples

Glossaire

octet
L'unité dans laquelle se compte le poids d'un fichier, comme un colis se compte en grammes. Mille octets font un kilo-octet, un million font un méga-octet — la taille d'une photo prise avec un téléphone. Un PDF d'une page pèse ici entre six mille et cent vingt mille, donc cent PDF tiennent dans la place de cinq photos.
point typographique
L'unité avec laquelle l'imprimerie mesure, un peu plus d'un tiers de millimètre : soixante-douze d'entre eux font un pouce. Le papier, les marges et la hauteur des lettres se comptent tous dedans, et chaque mesure écrite dans un fichier PDF aussi.
norme
Une règle débattue en commission, publiée sous un numéro que n'importe qui peut acheter et lire, et identique pour toutes les entreprises qui s'en réclament. Dire qu'on la suit peut donc être vérifié face au texte. Une façon de faire qui s'est simplement répandue parce qu'elle marchait est une habitude du métier : utile, très suivie, et qui ne répond devant aucun texte.
Factur-X
Une facture qui sert deux lecteurs à la fois. Pour la personne qui la reçoit, c'est une page ordinaire. Pour son logiciel de comptabilité, ce sont les mêmes montants rangés à l'intérieur du même fichier, sous une forme qu'il lit tout seul : plus rien à retaper. La loi française l'impose entre entreprises.
la carte d'identité d'un fichier
Le bloc écrit à l'intérieur d'un fichier qui dit ce qu'est ce fichier : son titre, qui l'a fait, quand, et quelles règles il suit. Les moteurs de recherche et les archives le lisent ; un lecteur ne le voit jamais. Son nom technique est XMP.
profil de couleur
Un fichier qui dit à quoi une couleur ressemble pour de vrai. Sans lui, le même rouge ne sort pas pareil sur un écran et sur du papier. Un document destiné à l'imprimerie emporte donc avec lui le profil sur lequel ses couleurs ont été choisies.
intention de sortie
Le profil de couleur écrit dans un document pour dire pour quelle presse ou pour quel écran ses couleurs ont été choisies. Un imprimeur le lit pour savoir à quoi doivent ressembler les chiffres du fichier, et les règles sur les documents gardés longtemps en réclament un.
police
Le dessin de chaque lettre, de chaque chiffre et de chaque signe qu'un document écrit, rangé dans un fichier à part. Un PDF emporte à l'intérieur de lui-même les polices dans lesquelles il est composé : c'est pour cela qu'il s'ouvre à l'identique sur une machine qui ne les a jamais eues. Sans elles, un lecteur en met une autre à la place, et la mise en page bouge.
dégradé
Une couleur qui change tout au long de l'espace qu'elle remplit, sans la moindre marche entre un bout et l'autre : un bandeau qui s'efface, une barre qui prend du relief. Elle est décrite une fois, en quelques nombres, et peinte partout où on la veut, si bien qu'elle reste nette à n'importe quelle taille et ne pèse presque rien.
requête
Un appel au service : vous envoyez ce que le document doit dire, et vous recevez le document. Votre facture compte ces appels, un par document. Le nombre de pages d'un document n'est jamais compté.
modèle
Un fichier PDF fait une fois et réemployé comme fond de pages neuves : votre papier à en-tête, une feuille vierge pour les pages qui suivent, la page de conditions que vous joignez toujours à la fin. Le fichier est posé exactement tel quel, au millimètre près, et le nouveau texte s'écrit par-dessus.
JSON
Une façon d'écrire une information structurée en texte simple, faite de valeurs nommées, de listes et de nombres. Tous les langages de programmation le lisent et l'écrivent, et c'est pourquoi c'est ce qu'un programme emploie pour décrire le document qu'il veut.
serveur
Une machine qui attend des appels et y répond, jour et nuit, sans personne devant elle. Celle qui dessine les documents ici porte le moteur et rend les pages finies ; celle où tourne votre site est une autre machine qui fait le même genre de travail.
code de statut
Le nombre à trois chiffres par lequel une réponse commence, et qui dit comment l'appel s'est passé. Deux cents veut dire que cela a marché. Tout ce qui est dans les quatre cents veut dire que l'appel est à changer ; tout ce qui est dans les cinq cents veut dire que la machine qui répond a quelque chose à corriger.
formulaire multipart
Une façon d'emballer plusieurs choses dans un seul appel : chacune reçoit un nom et voyage dans sa propre partie, et ce peut être du texte comme des fichiers. C'est ce qu'emploie une page web pour envoyer une photographie, et ce qui porte la description d'un document à côté des images qu'il dessine.
calque
Un groupe nommé de choses dessinées sur une page, que le lecteur peut afficher ou masquer, comme un calque posé sur un plan. Un plan peut garder ses cotes sur l'un et ses notes sur l'autre, et un calque peut être réglé pour s'afficher à l'écran et rester hors de l'impression.
filigrane
Une mention imprimée en travers de la page, qui indique que le document a été fabriqué avec un compte gratuit. Elle ne peut pas être effacée du fichier ; les formules payantes ne la posent tout simplement pas.

Tous les mots que le site explique