Die API, die Ihre PDFs erzeugt

Ihr Programm beschreibt das gewünschte Dokument in JSON, sendet es in einem Aufruf, und das fertige PDF kommt in der Antwort zurück.

Zusammenfassung

Denken Sie an eine Tortenbestellung. Sie schreiben auf, was Sie darauf haben möchten, reichen diese Beschreibung über die Theke, und die Torte kommt einen Moment später über dieselbe Theke zurück. Sonst geschieht nichts, und die Küche sehen Sie nie.

Die Beschreibung ist hier ein Text, den Ihr Programm schreibt. Er sagt, was auf die Seite kommt: dieser Satz an diese Stelle, diese Tabelle darunter, dieses Logo in die Ecke, dieser QR-Code an den Fuß. Alles, was die Seite braucht und der Text nicht fassen kann — ein Foto, eine Briefkopfvorlage —, reist daneben mit.

Die Beschreibung wird in einer Form geschrieben, auf die sich jede Maschine einigt, genannt JSON. Es ist gewöhnlicher Text aus Namen und Werten, und er liest sich gleich, in welcher Sprache Ihr Programm selbst auch geschrieben ist.

Zurück kommt das fertige Dokument, bereit, an einen Kunden oder eine Druckerei gesendet zu werden. Es kommt in der Antwort auf den Aufruf selbst zurück, sofort.

Die Antwort ist das Dokument. Kein Link, um es später abzuholen, keine Ticketnummer, nach der man erneut fragen muss: Die Bytes des fertigen PDF kommen in der Antwort auf diesen einen Aufruf zurück.

Um diese Adresse aufzurufen, wird nichts installiert: Ihr Programm weiß bereits, wie es etwas über das Netz sendet, und mehr wird von ihm nicht verlangt.

Es gibt fünfundzwanzig Dinge, die Sie auf eine Seite setzen können: Absätze, Tabellen, Fotos, aus einem anderen Dokument übernommene Seiten, die fünf Arten von Barcodes und quadratischen Codes, schlichte Formen, die Felder eines auszufüllenden Formulars und anklickbare Links. Jedes davon hat hier eine eigene Seite, mit dem kürzesten Beispiel, das es zeichnet.

Daneben legen achtzig Schlüssel das Dokument um das Gezeichnete herum fest: die Liste dessen, was auf die Seiten kommt, und daneben die Blattgröße, die Schriften, was die Datei über sich selbst sagt, wie ein Leser sich darin zurechtfindet, die Farben und wie alles herauskommt. Jeder davon hat ebenfalls eine eigene Seite, dieser Abschnitt umfasst also hundertfünf Seiten: eine für jedes Ding, das Sie auf eine Seite setzen können, und eine für jeden Schlüssel.

Zwei Teile, und warum es zwei sind

Der Server erzeugt selbst keine PDF-Datei. Er liest die Beschreibung, prüft sie und ruft dann die Bibliothek Anweisung für Anweisung auf: diesen Absatz setzen, diese Tabelle beginnen, dieses Bild hier platzieren. Die Bibliothek schreibt die Datei.

Es sind zwei, weil sie zwei verschiedene Aufgaben erfüllen. Die Bibliothek kann zeichnen und weiß nichts vom Netz. Der Server kennt das Netz — wer aufruft, wie groß eine Anfrage sein darf, wie viele Seiten ein Konto haben darf — und weiß nichts vom Zeichnen.

Die Bibliothek arbeitet mit bereits geladenen, zeichenbereiten Schriften, während eine über das Netz ankommende Beschreibung nur ihren Namen trägt. Diesen Namen in eine Schrift zu verwandeln, die die Bibliothek verwenden kann, ist genau die Aufgabe des Servers, und sie ist erledigt, bevor der erste Buchstabe auf einer Seite landet.

Deshalb lässt sich die Bibliothek eigenständig kaufen und nutzen, direkt aus einem in Rust oder Python geschriebenen Programm aufgerufen, ohne Server und ohne dass eine Beschreibung irgendwohin reist. Und deshalb kommt ein Dokument auf beiden Wegen gleich heraus, Seite für Seite: Dieselbe Bibliothek schreibt es.

Das Erzeugen eines PDF beschäftigt einen Prozessor von Anfang bis Ende; jede Anfrage wird daher von einem Prozess bearbeitet, der sonst nichts zu tun hat, und der Server antwortet in der Zwischenzeit allen anderen weiter.

Der Hin- und Rückweg, gezeichnet

  1. Ihr eigenes Programm läuft auf Ihren eigenen Maschinen
  2. Der Server an der Adresse, die Sie aufrufen
  3. Die Bibliothek im Server

Was beim Erzeugen des PDF bemerkt wurde

Neben dem fertigen Dokument sagt die Antwort, was beim Erzeugen des PDF aufgefallen ist. Es ist ein Hinweis, keine Ablehnung: Die Seiten sind die Seiten, die Sie verlangt haben, und ein Hinweis lässt sie genau so, wie sie sind.

Die Antwort sagt immer, wie viele Hinweise es gab, und eine Null ist eine eigene Antwort: Beim Erzeugen des PDF ist nichts aufgefallen.

Zwei Dinge werden heute vermerkt. Das erste ist etwas, das Ihre Beschreibung mitgesendet hat und das keine Seite je verwendet hat — eine Schrift, ein Foto, eine Farbe, eine Ebene. Es ist mitgereist und in die Datei gekommen, die Datei ist also schwerer, als die Seiten es brauchten.

Die zweite ist eine Schrift mit einem Lizenzvermerk dessen, der sie gezeichnet hat, der sagt, wie sie weitergegeben werden darf. Das Dokument wird mit dieser Schrift darin geschrieben, und der Hinweis wiederholt, was der Lizenzvermerk sagt, damit Sie ihn mit der Lizenz vergleichen können, unter der Sie diese Schrift gekauft haben. Diese Lizenz gehört Ihnen und bleibt bei Ihnen: Sie ist das Einzige, was die Datei nicht tragen kann.

Höchstens zehn Hinweise kommen in der Antwort zurück, während die Zählung sagt, wie viele es wirklich waren. Den Rest kann man nicht abrufen: Der Server schreibt jeden davon in sein eigenes Protokoll.

Technisch

Ein POST /render mit multipart/form-data: das als JSON beschriebene Dokument im Teil json_data, die Dateien, die es nennt, in den Teilen daneben, und zurück entweder 200 application/pdf oder eine nummerierte Ablehnung in Klartext.

Die Übertragung

Ein Endpunkt, eine Methode: ein POST mit einem multipart/form-data-Body. Der Teil namens json_data enthält die ganze Beschreibung als JSON. Jeder andere Teil ist eine Datei, die die Beschreibung unter dem Namen dieses Teils anspricht — ein PDF als Vorlage oder seitenweise importiert, ein Bild, ein ICC-Profil, ein XMP-Paket, das XML einer Rechnung. Es gibt keinen Upload-Schritt und keine Asset-Kennung, die man aufbewahren müsste: Ein Teil besteht nur für den Aufruf, der ihn getragen hat.

Die Authentifizierung ist ein API-Schlüssel im Header Authorization, den diese Website prüft, einem Kundennamen zuordnet und in einem eigenen Header weiterleitet. Ein Erfolg antwortet mit 200, application/pdf und der Seitenzahl. Eine Ablehnung antwortet mit einem Statuscode, einem Satz in Klartext und x-hqf-refusal: einem Wort aus einer geschlossenen Liste, das die Art der Ablehnung nennt und das ein Programm liest. Es gibt keine Hülle zum Auspacken und keinen Auftrag zum Abfragen. Der von uns betriebene Dienst beantwortet eine Ablehnung stattdessen in JSON: Unter kind reicht er das Wort des Servers weiter, und unter error schreibt er einen eigenen Satz, der den des Servers zitiert.

Der von uns betriebene Dienst lehnt auch aus eigenem Antrieb ab, unter eigenen Wörtern, die die Seite seiner Adresse auflistet. Eines davon stammt aus der Monatszählung: Wenn die Dokumente eines Monats alle verbraucht sind, antwortet er mit 429, wobei Retry-After das Datum des Monatswechsels trägt. Ein eigener Server zählt keinen Monat.

Woraus ein Aufruf und seine Antwort bestehen
Der Aufruf, der ein Dokument erzeugt, auf einem eigenen Server POST /render
Der von uns betriebene Dienst, der einen eigenen Aufruf annimmt POST https://hqf-pdf.com/api/v1/render
Der Aufruf, der ein von Ihnen gesendetes PDF ergänzt, auf einem eigenen Server POST /update
Derselbe Aufruf beim von uns betriebenen Dienst POST https://hqf-pdf.com/api/v1/update
Der Aufruf, der sagt, dass der Dienst läuft GET /health
Die Form, in der der Body gesendet wird multipart/form-data
Der Teil mit der Beschreibung json_data
Jeder andere Teil eine Datei, die die Beschreibung per Name anspricht
Wie der API-Schlüssel übertragen wird Authorization: Bearer <key>
Was diese Website an den Server weiterleitet x-hqf-client: <name>
Was ein Erfolg trägt 200 application/pdf
Wie viele Seiten das fertige Dokument hat x-hqf-pages
Wie viele Dinge beim Erzeugen des PDF aufgefallen sind x-hqf-warnings
Ein Hinweis, einmal pro enthaltenem Hinweis wiederholt x-hqf-warning
Wie viele Befunde das Dokument von den verlangten Normen trennen x-hqf-findings
Ein Befund, einmal pro enthaltenem Befund wiederholt x-hqf-finding
Was ein Fehler trägt text/plain
Das Wort, mit dem eine Ablehnung ihre Art benennt x-hqf-refusal
Was eine Ablehnung bei dem von uns betriebenen Dienst trägt, dasselbe Wort eingeschlossen application/json {"error": "…", "kind": "…"}
Die Version des Dienstes, der geantwortet hat x-hqf-version

Der Aufruf, den der von uns betriebene Dienst annimmt, und jede Antwort, die er gibt

Der Body, Teil für Teil

Der Body ist ein Formular in mehreren Teilen, und jeder Teil hat einen Namen.

  • json_data enthält die Beschreibung des Dokuments, mit ihren achtzig Schlüsseln. Es ist der einzige Teil, dessen Name feststeht.
  • Jeder andere Teil ist eine Datei, auf die die Anfrage zurückgreift, unter dem Namen, unter dem die Beschreibung sie aufruft: ein unter die Seiten gelegtes PDF, von templates genannt, ein Bild, von einem image-Element genannt, ein ICC-Profil, von output_intent oder einem icc-Farbraum genannt, das XML, von invoice genannt, ein Metadatenpaket, von einem Bild genannt. Was ein Teil ist, ergibt sich aus dem JSON, das ihn nennt, nie aus dem Teil selbst.
  • Der Body wird vollständig im Speicher gehalten, und seine Größe ist auf 536 870 912 Bytes begrenzt — 512 MiB —, sofern eine Einstellung am Server nichts anderes sagt.
  • Der von uns betriebene Dienst begrenzt den Body eines eigenen Aufrufs auf 536 870 912 Bytes, also 512 MiB, und antwortet darüber mit 413 und body_too_large. Die beiden Obergrenzen sind zwei eigene Einstellungen, ein von Ihnen betriebener Server kann also niedriger oder höher eingestellt sein als der obige.

Der Header, den diese Website setzt

Der Server authentifiziert niemanden: Diese Website nimmt den Aufruf an, prüft den API-Schlüssel und nennt den Kunden, dem er gehört, im Header x-hqf-client. Der Server liest diesen Namen und erzeugt das PDF unter dem, was das Konto trägt: seine Schriften, seine Seitenobergrenze, seine Berechtigungen. Eine Anfrage, die einen Kunden nennt, den der Server nicht kennt, oder keinen nennt, wird mit den Standardeinstellungen erzeugt. Dem Header kann man vertrauen, weil nichts außer dieser Website ihn setzen kann: Der Server lauscht auf dem Loopback, und nichts außerhalb dieser Maschine erreicht ihn, und diese Website schreibt den Header bei jeder weitergeleiteten Anfrage neu.

Was ein Erfolg zurückgibt

  • Der Statuscode 200 und die Bytes des PDF als Body.
  • Content-Type: application/pdf.
  • x-hqf-pages, wie viele Seiten das fertige Dokument hat.
  • x-hqf-warnings, zu wie vielen Dingen die Engine beim Schreiben des Dokuments einen Hinweis gemacht hat, und ein x-hqf-warning pro enthaltenem Hinweis.
  • x-hqf-findings, wie viele Befunde zwischen dem fertigen Dokument und den Normen stehen, an denen check es messen ließ, und ein x-hqf-finding pro enthaltenem Befund. Beide stehen in jeder Antwort, die ein Dokument enthält, ob eine Norm angefordert wurde oder nicht.
  • Ein eigener Server wiederholt den Header einmal pro Wert. Der von uns betriebene Dienst sendet den Namen einmal mit durch Kommas getrennten Werten; die Regel zum Aufteilen steht auf der Seite dieses Dienstes.

Was eine Erzeugung vermerkt

Ein Hinweis ist etwas, das der Engine beim Schreiben eines Dokuments auffiel, das sie trotzdem geschrieben hat: Die zurückgegebenen Bytes sind die Bytes, die eine Erzeugung ohne Hinweis zurückgegeben hätte, und der Statuscode bleibt 200. x-hqf-warnings gibt an, wie viele es waren, und steht in jeder Antwort mit erzeugtem Dokument, auch bei 0; eine Antwort ohne diesen Header stammt von einem Server, der älter ist als der Header selbst.

x-hqf-warning wird dann einmal pro enthaltenem Hinweis wiederholt, in der Reihenfolge, in der die Engine sie gemacht hat. Höchstens zehn, zusammen bis zu tausend Bytes, je nachdem, was zuerst erreicht ist. Der Hinweis, der über die verbleibenden Bytes hinausläuft, wird gekürzt und endet auf %E2%80%A6, einer Auslassung, und die Hinweise danach stehen allein im Protokoll des Servers — das jeden davon vollständig enthält, auf INFO, unter dem Namen des Kunden, für den erzeugt wurde. Die Zählung nennt in jedem Fall die Gesamtzahl, sodass ein Kunde, der x-hqf-warnings: 50 neben zehn Headern liest, genau weiß, woran er ist.

Diese beiden Zahlen halten die Antwort zustellbar. Ein Frontend-Server begrenzt, wie groß die Header einer Antwort sein dürfen, und ein Dokument, das fünfzig Ressourcen deklariert, die es nie zeichnet, ergäbe fünfzig Header: Die ganze Antwort würde abgewiesen, bevor sie den Kunden erreicht, und ein fehlerfrei geschriebenes Dokument käme als Ausfall des Dienstes an.

  • Eine von der Anfrage deklarierte Ressource, mit der keine Seite gezeichnet hat — eine Schrift, ein Bild, eine Zeichnung, ein Farbraum, ein Grafikzustand, ein Verlauf, ein Muster oder eine Ebene. Sie wird in die Datei geschrieben und jeder Seite zur Verfügung gestellt, und kein Inhaltsstream nennt sie. Der Hinweis lautet the image Im2 is never drawn with.
  • Eine entgegen ihrem eigenen fsType-Flag eingebettete Schrift: ein Flag, das die Einbettung verbietet, eines, das verlangt, vollständig eingebettet zu werden, wo nur die gezeichneten Glyphen eingebettet wurden, oder eines, das nur die in der Schrift enthaltenen Bilder erlaubt, wo Konturen eingebettet wurden. Der Hinweis nennt die Schrift so, wie sie sich selbst nennt, und den Namen, unter dem die Anfrage sie auswählt — the font Foo (F1) must not be embedded without the owner's permission. Ob die Einbettung rechtmäßig ist, hängt von der Lizenz ab, unter der die Schrift gekauft wurde; diese zu lesen ist Ihre Sache.
  • Jeder Wert ist prozentkodiert und dekodiert sich wie eine URL: Jedes Byte außerhalb des druckbaren ASCII steht als %XX und das Prozentzeichen selbst als %25, sodass Café als Caf%C3%A9 ankommt. Ein Header trägt nur ASCII, während eine Ressource von dem benannt wird, der die Anfrage geschrieben hat, und eine Schrift sich in ihrer eigenen Lizenz nennt — ein Hinweis enthält also, was diese beiden enthalten. Ein gekürzter Hinweis wird zwischen zwei Zeichen gekürzt, sodass das Ankommende sich dekodieren lässt und Text ergibt.

Die antwortende Version und die Zustandsabfrage

GET /health antwortet mit 200 und einem kleinen JSON-Objekt: version, die Nummer des antwortenden Builds, licensed, ob eine Lizenz den Tag abdeckt, und faces, wie viele Schriften die allgemeine Bibliothek enthält. Das liest ein Überwachungsprogramm oder ein Load Balancer, um zu wissen, dass der Dienst antwortet, und das liest ein Deployment, um zu wissen, dass der installierte Build nun derjenige ist, der antwortet.

Dieselbe Nummer steht in jeder Antwort, die der Dienst sendet, im Header x-hqf-version: bei einem erzeugten Dokument, bei einer Ablehnung und bei der Abfrage gleichermaßen. Ein Kunde schreibt sie in sein eigenes Protokoll neben den Aufruf, den er gemacht hat, sodass ein falsch zurückgekommenes Dokument mit dem Build verknüpft ist, der es erzeugt hat; und nach einem Deployment wird sie gelesen, um eine ersetzte Instanz von einer zurückgebliebenen zu unterscheiden. Eine Antwort ohne diesen Header stammt von einem Build, der älter ist als der Header selbst. Die Nummer gehört dem Dienst: Die Engine trägt eine eigene, auf der Seite der Bibliothek, und diese Website trägt eine dritte, am Fuß jeder Seite.

Das Modell

Eine Anfrage hat eine oberste Ebene: eine Liste von Elementen und daneben die Ressourcen, die diese Elemente nennen. Schriften, Farbräume, Verläufe, Grafikzustände, Ebenen und Zeichnungen werden jeweils einmal unter einem Namen deklariert, und ein Element verweist auf diesen Namen. Eine Ressource wird über ihren Namen gefunden, wo immer sie deklariert ist, und alles, was zählt, wird geschrieben — ein Name, den ein Element verlangt und den keine Deklaration trägt, wird abgelehnt, mit dem Namen in der Meldung.

Seiten entstehen durch das, was auf ihnen gezeichnet wird, nicht durch vorherige Deklaration. Ein Element gibt an, auf welchen Seiten es landet; der Standard, ein leeres Array, ist jede Seite, und so wird eine Fußzeile oder ein Wasserzeichen einmal geschrieben. Eine Tabelle, die über ihren Kasten hinausläuft, setzt sich im nächsten Kasten fort und erzeugt die Seiten, die sie braucht, und die an jede Seite gebundenen Elemente folgen ihr dorthin.

Die Geometrie ist in typografischen Punkt angegeben, mit dem Ursprung links unten, wie es das Format selbst vorsieht. Jede Koordinate darf ein in Anführungszeichen gesetzter arithmetischer Ausdruck über die Seitenmaße und die aktuelle Seitenzahl sein, sodass ein rechter Rand "{page_width} - 56" ist statt einer Zahl, die Ihr Programm ausrechnen musste. Ein Element kann sich benennen, und ein anderes kann an seiner Kante hängen; so bleibt ein Layout richtig, wenn die Höhe eines Blocks erst nach dem Setzen bekannt ist.

Was eine Anfrage neben dem trägt, was sie erzeugt

Dieselbe Anfrage gibt die Metadaten des Dokuments an, die beanspruchte Archivierungsnorm, den Output Intent, an dem seine Farben gelesen werden, die Kompression seiner Streams, die Gliederung und die benannten Ziele, die Artikelflüsse, die Seitenbeschriftungen, die Leseeinstellungen, die die Anzeigesoftware beachtet, und das Rechnungs-XML, das ein Factur-X-Dokument trägt. Ein Aufruf erzeugt eine fertige, normbeanspruchende Datei: Es gibt keinen zweiten Durchgang und kein Nachbearbeitungswerkzeug.

Die Antworten und was sie sagen

Die Validierung ist vollständig und erfolgt, bevor etwas gezeichnet wird: Der Server lehnt eine Anfrage ab, statt ein Dokument zu erzeugen, das stillschweigend etwas verloren hat. Eine Ablehnung ist ein Satz, der den fehlerhaften Schlüssel oder Namen nennt; es lohnt sich also, sie wörtlich zu protokollieren.

Daneben steht x-hqf-refusal, ein Wort aus einer geschlossenen Liste: client_name, call_number, form, body_too_large, description, drawing, too_many_pages, needs_a_licence, server. Danach verzweigt ein Programm, denn der Satz ist für einen Menschen geschrieben. Ein Wort, das nicht auf dieser Liste steht, stammt von einem Server, der neuer ist als diese Seite. Der von uns betriebene Dienst reicht diese Wörter unverändert unter kind weiter, neben eigenen Wörtern, die die Seite seiner Adresse auflistet.

Unbekannte Schlüssel werden fast überall ignoriert; deshalb lässt sich eine für eine spätere Version geschriebene Anfrage gefahrlos an eine frühere senden. Die eine Ausnahme ist die Objektform von pages, die aus every, from, to und but gebaute Regel: Dort wird ein Schlüssel, den die Regel nicht hat, namentlich abgelehnt, da eine Regel, die nichts angibt, das ganze Dokument abdeckt und ein falsch geschriebener Schlüssel stillschweigend dasselbe täte. Ein zweimal im selben Objekt geschriebener Schlüssel wird namentlich abgelehnt. Kombinationen, die einander widersprächen — eine Matrix zusammen mit einer Drehung, ein Link mit kurzem Ziel und vollständiger Aktion zugleich, eine Tabellenzelle mit zwei Inhalten —, werden ebenfalls namentlich abgelehnt.

Eine Seite, die das Dokument nicht hat, wird abgelehnt, wo immer ein Schlüssel eine nennt — ein Lesezeichen, ein benanntes Ziel, der Bereich eines Artikelflusses —, und die Meldung nennt diese Seite in der Nummerierung der Anfrage: Fragen Sie nach Seite neun eines fünfseitigen Dokuments, sagt die Meldung Seite neun.

Diese Website wartet 30 Sekunden auf das Dokument und nicht länger. Danach kommt der Aufruf mit dem Status 503 und einem Satz zurück, der sagt, dass der Dienst nicht geantwortet hat. Wie lange ein eigenes Dokument braucht, wird nirgends veröffentlicht: Messen Sie es an Ihren eigenen Seiten, auf der Maschine, die sie ausführen wird.

Jede Antwort des Dienstes, nach ihrer Nummer
200 Das PDF ist der Body. Daneben stehen die Seitenzahl und was beim Erzeugen des PDF aufgefallen ist: eine Zählung und ein Header pro enthaltenem Hinweis.
400 Die Anfrage muss korrigiert und erneut gesendet werden; der Body ist ein Satz in Klartext. Bekannte Fälle: Das JSON lässt sich nicht lesen; der Teil json_data fehlt; eine genannte Schrift ist nirgends deklariert; eine Koordinate lässt sich nicht parsen; ein Link oder ein Lesezeichen führt zu einer Seite, die das Dokument nicht hat, genannt in der Nummerierung der Anfrage selbst; eine angegebene Norm und eine Einstellung, die diese Norm ausschließt, widersprechen sich; eine Kompressionsstufe liegt außerhalb von 1 bis 9; eine mitgelieferte Vorlage ist kein lesbares PDF.
402 Die Anfrage verlangt etwas, das nur ein Lizenzschlüssel abdeckt, unter x-hqf-refusal: needs_a_licence oder beim von uns betriebenen Dienst unter kind. Der Satz nennt, was verlangt wurde, und sagt, dass ein Lizenzschlüssel es mitbringt. Alle Fälle: Text in einer Schrift ohne Kontur, aus der sich die Glyphen zeichnen ließen — die vierzehn Standardschriften, eine Schrift, deren Glyphen Zeichnungen sind, ein Type-1-Programm; eine aus einem anderen Dokument übernommene Seite, die mehr als zehntausend eigene Wörter zeigt; eine übernommene Seite, deren Wörter sich gar nicht zählen lassen; eine Angabe zur Barrierefreiheit oder zur Archivierung auf der Stufe, die darauf beruht, zu sagen, wofür die Markierungen auf einer Seite stehen; ein Ziel, das zu einem Element des Strukturbaums führt. Die letzten drei ergeben sich daraus, dass eine kostenlose Kopie jede Glyphe als Form nachzeichnet: Das Dokument hat keinen Strukturbaum, auf dem eines davon beruhen könnte.
413 Der Body der Anfrage überschreitet die Größe, die der Server annimmt, oder das Dokument überschreitet die Seitenobergrenze des Servers oder des Kontos. Der Satz nennt die Größe oder die Obergrenze.
500 Das Schreiben des PDF oder des Schriftenspeichers ist fehlgeschlagen. Das liegt auf der Seite des Dienstes, nicht in der Anfrage.

Anfrageschema

Jeder Schlüssel, den eine Anfrage trägt, mit seinem Typ, ob er erforderlich ist und worauf er zurückfällt, wenn er weggelassen wird. Jede Zahl wurde aus den Quellen des Servers abgelesen und gilt für die Version im Produktivbetrieb.

Die kleinste Anfrage, die ein PDF erzeugt

Eine deklarierte Schrift, ein Element, eine Textzeile. Speichern Sie es, senden Sie es, und ein einseitiges PDF kommt zurück.

{
  "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
    }
  ]
}

Derselbe Aufruf, dreimal geschrieben

Keiner der drei ist eine zu installierende Bibliothek: Der Server antwortet über das Netz, also ruft ihn jede Sprache auf, die ein Formular senden kann. Hier sind drei — eine Shell, Python und JavaScript.

Kopieren Sie, was zu Ihrem Stack passt. Jedes sendet die Beschreibung und eine Datei, auf die sie zurückgreift, und schreibt die Antwort direkt auf die Platte.

Was die Rust-Bibliothek und die Python-Bibliothek erzeugen

Aus einer Shell

Ein Teil trägt die Beschreibung, ein Teil pro Datei, auf die sie zurückgreift. Der Name eines Teils ist der Name, unter dem die Beschreibung diese Datei aufruft.

# The service we run takes a call of its own:
# POST https://hqf-pdf.com/api/v1/render described on
# https://hqf-pdf.com/en/api/pdf-api-endpoint/
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

Aus Python

Der API-Schlüssel kommt aus der Umgebung, und die Antwort ist das PDF selbst, schreiben Sie den Body also direkt in eine Datei. Die Seitenzahl steht in jeder Antwort, die ein Dokument trägt.

import json
import os
import pathlib

import requests

KEY = os.environ["HQF_PDF_KEY"]
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,
        }
    ],
}

# The service we run takes a call of its own: POST https://hqf-pdf.com/api/v1/render
# described on https://hqf-pdf.com/en/api/pdf-api-endpoint/
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-pages"], "pages")

Aus JavaScript

Ein Formular-Body und das Standard-fetch, ausgeführt von Node. Der API-Schlüssel kommt aus der Umgebung und die Vorlage aus einer Datei neben dem Programm, und eine Ablehnung kommt als Satz in Klartext an.

import { readFile, writeFile } from "node:fs/promises";

const key = process.env.HQF_PDF_KEY;
const letterhead = new Blob([await readFile("letterhead.pdf")]);

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", letterhead, "letterhead.pdf");

// The service we run takes a call of its own:
// POST https://hqf-pdf.com/api/v1/render
// described on https://hqf-pdf.com/en/api/pdf-api-endpoint/
const answer = await fetch("https://your-server/render", {
  method: "POST",
  headers: { Authorization: `Bearer ${key}` },
  body,
});
if (!answer.ok) {
  throw new Error(await answer.text());
}
await writeFile("invoice.pdf", new Uint8Array(await answer.arrayBuffer()));

Die Schlüssel einer Anfrage

Auf der obersten Ebene einer Anfrage stehen achtzig Schlüssel, und keiner davon ist Pflicht: Für jeden gilt der Standardwert aus der nebenstehenden Spalte, sodass eine Anfrage nur angibt, was sie braucht. Jeder Schlüssel führt zu einer eigenen Seite.

Jeder JSON-Schlüssel, den eine Anfrage auf oberster Ebene trägt
JSON-Schlüssel Typ Standard Wofür es da ist
items Array [] Alles, was auf den Seiten gezeichnet wird, in Zeichenreihenfolge.
page Objekt 595.276 × 841.89 Das Blatt, auf das jede Seite zugeschnitten wird, in Punkt, sofern eine Seite nicht ihr eigenes nennt.
page_sizes Array [] Ein eigenes Blatt für eine nummerierte Seite.
page_boxes Array [] Crop-, Bleed-, Trim- und Art-Box einer Seite; das liest eine Druckerei vor allem anderen.
page_turns Array [] Wie herum eine Seite dem gezeigt wird, der die Datei öffnet.
page_tab_orders Array [] Die Reihenfolge, in der die Tastatur die Felder einer Seite durchläuft.
page_shows Array [] Wie eine Seite erscheint und wie lange sie stehen bleibt, wenn die Anzeigesoftware das Dokument im Vollbild zeigt.
source Objekt — Ein mit der Anfrage gesendetes PDF, mit dessen Seiten das Dokument beginnt, jede in ihrer eigenen Größe, unter allem, was die Elemente zeichnen.
supplied_pdfs Objekt — Wie die neben der Anfrage mitgelieferten PDF gelesen werden: wie weit sich einer ihrer Streams entpacken darf.
structure Objekt — Woraus das Dokument laut eigener Angabe besteht, in Lesereihenfolge, sodass ein Programm es liest, wie ein Mensch es täte, statt wie die Farbe aufgetragen wurde.
accessible Zeichenkette — Die Barrierefreiheitsnorm, die das Dokument beansprucht, geprüft gegen das, was es tatsächlich enthält.
simple_fonts Array [] Eine über Ein-Byte-Codes angesprochene Schrift, zugeschnitten auf einen angegebenen Text: eine leichtere Seite und ein Wortabstand, der einen Code erreichen kann.
color_fonts Array [] Eine Schrift, deren ausgewählte Zeichen in den Farben erscheinen, in denen ihre Datei sie malt, Schicht für Schicht, aus einer ihrer Paletten.
fonts_described_only Array [] Die Schriften, von denen das Dokument nur die Beschreibung enthält, ohne ihr Programm.
font_permissions Zeichenkette warn Was eine Schrift, deren eigenes Kennzeichen das Mitführen verbietet, mit dem Dokument macht.
whole_font_ceiling Ganzzahl — Das Gewicht in Bytes, bis zu dem eine Schriftdatei vollständig in das Dokument kommt.
attachments Array [] Die Anhänge, die das Dokument neben seinen Seiten trägt.
referenced_files Array [] Die Dateien, die das Dokument nennt, ohne sie zu enthalten, daneben gespeichert oder unter einer Adresse ausgeliefert.
collection Objekt — Wie die Anzeigesoftware die Anhänge als Portfolio anordnet: die Spalten, die Reihenfolge der Zeilen und die zuerst geöffnete Datei.
page_files Array [] Welche der Anhänge eine Seite als ihre eigenen beansprucht.
font_files Array [] Welche der Anhänge eine Schrift als ihre eigenen beansprucht.
font_metadata Array [] Der Ausweis, den eine Schrift trägt.
metadata_files Array [] Welche der Anhänge die eigene Karte des Dokuments beansprucht.
metadata_packet Zeichenkette — Der mitgelieferte Teil mit dem eigenen Ausweis des Dokuments, anderswo geschrieben und unverändert übernommen.
page_viewports Array [] Die Bereiche einer Seite, in denen ein Leser misst, und in welchen Einheiten.
page_metadata Array [] Der Ausweis, den eine Seite trägt, neben dem des Dokuments.
tilings Array [] Eine Zelle, wiederholt, um eine Form zu füllen: Schraffur, Gewebe, Hintergrund.
fonts Array [] In der Anfrage als Base64 mitgebrachte Schriften, jede unter einem Namen, den die Elemente aufrufen, mit den Zeichen, die sie behalten, und dem, was sie für ein Zeichen zeichnen, für das sie keine Glyphe haben.
type1_fonts Array [] Type-1-Schriften, auf dieselbe Weise getragen.
standard_fonts Array [] Eine der vierzehn Schriften, die jede Anzeigesoftware bereits mitbringt, unter einem Namen Ihrer Wahl.
font_variants Array [] Eine deklarierte Schrift mit eingeschalteten Ligaturen, Kapitälchen, Mediävalziffern oder Unterschneidung.
type3_fonts Array [] Eine Schrift, deren Glyphen Zeichnungen statt Konturen sind.
font_chains Array [] Zwei oder mehr Schriften, der Reihe nach versucht, sodass eine Glyphe, die der ersten fehlt, aus der nächsten genommen wird.
templates Objekt — Die mitgelieferten PDF, die unter die erste Seite, unter die mittleren Seiten und unter die letzte gelegt werden.
watermarks Array [] Eine Zeile, die das Dokument über eine Reihe seiner Seiten legt, über oder unter das, was sie sagen.
renumber Array [] Wo die Seitennummerierung neu beginnt, in welchem Stil, hinter welchem Präfix.
bookmarks Array [] Die Gliederung, die die Anzeigesoftware neben der Seite öffnet, so tief verschachtelt, wie Sie möchten.
destinations Array [] Benannte Orte im Dokument, auf die ein Link oder eine Aktion zielen kann.
articles Array [] Artikelflüsse, die einen Leser von einer Spalte zur nächsten führen.
document_parts Objekt — Der Baum aus Teilen, den ein PDF-2.0-Dokument deklariert, jeder beansprucht einen Seitenbereich und trägt benannte Werte.
private_data Objekt — Was ein Programm für sich selbst im Dokument hinterlässt, unter seinem eigenen Namen abgelegt. Keine Anzeigesoftware zeigt es.
page_private_data Array [] Dasselbe, auf einer nummerierten Seite statt am Dokument hinterlassen.
page_regions Array [] Benannte Rechtecke auf einer Seite, die eine spätere Anfrage über ihre Namen füllt.
metadata Objekt — Titel, Autor, Thema, Stichwörter, das Programm, in dem es verfasst wurde, das erzeugende Programm, Erstellungs- und Änderungsdatum, Überfüllungsstatus und eigene Schemata.
information_dictionary boolescher Wert true Ob die Datei ihre Eintragstabelle neben ihrem Metadatenpaket behält oder allein im Paket sagt, was sie ist.
text_as_shapes boolescher Wert false Ob jeder Buchstabe als Form gezeichnet wird, sodass sich auf den Seiten nichts markieren oder kopieren lässt.
archive Zeichenkette — Die Archivierungsnorm, die die Datei beansprucht: `pdfa3b` oder `pdfa4`.
protection Objekt — Die Passwörter, unter denen das Dokument verschlossen ist, und was die Anzeigesoftware erlauben soll, sobald es offen ist.
invoice Objekt — Das im Dokument getragene Rechnungs-XML, mit seinem Profil, seiner Version, seiner Beziehung und seinem Datum.
reading Objekt — Wie die Anzeigesoftware gebeten wird, die Datei zu öffnen: welche Seitenleiste, welches Layout, welche Seite, welche Vergrößerung und was an einen Drucker gesendet wird.
base_uri Zeichenkette — Die Adresse, gegen die ein relativer Link aufgelöst wird.
version Zeichenkette das der Engine Die PDF-Version, die die Datei angibt: `1.4` bis `1.7` oder `2.0`.
extensions Array [] Die PDF-Erweiterungen, die die Datei nach eigener Angabe verwendet, jede unter ihrem Präfix.
compression Objekt — Ob Streams mit Deflate komprimiert werden, auf welcher Stufe und ob kleine Objekte zusammengepackt reisen.
first_page_first boolescher Wert false Ob die Datei mit der ersten Seite zuerst geschrieben wird, sodass eine Anzeigesoftware Seite eins zeigt, während der Rest noch ankommt.
output_intent Objekt sRGB Das Farbprofil, an dem die Farben der Datei gelesen werden sollen.
images Array [] Was ein mitgeliefertes Bild über sich sagt und wie es gelesen wird: seine Masken, seine durchsichtigen Abtastwerte, seine Ausrichtung und sein Profil.
color_spaces Array [] Separationen, indizierte Paletten, Lab, kalibriertes Grau und RGB und profilbasierte Farbräume, jeder unter einem Namen, den eine Farbe verlangen kann.
shadings Array [] Axiale und radiale Verläufe, jeder unter einem Namen, den eine Füllung verlangen kann.
graphics_states Array [] Benannte Zustände mit den Deckkräften, der Füllmethode, einer Maske und dem, was eine Druckmaschine verlangt: das Überdrucken, der Intent, die Transferkurve und das Raster.
layers Array [] Benannte Ebenen, die die Anzeigesoftware ein- und ausschalten kann und die sagen, ob sie gedruckt werden.
drawings Array [] Benannte Vektorzeichnungen; so etwas zeigt eine Schaltfläche als Symbol.
svg_drawings Array [] Aus SVG-Dateien gelesene Zeichnungen, über ihren Namen gezeigt wie benannte Zeichnungen.
transparency Objekt — Der Mischfarbraum, in dem die Seitengruppe arbeitet, und ob sie isoliert oder ausgespart ist.
page_units Array — Wie viele Punkt eine Einheit einer Seite ist, für ein Blatt, das größer ist als die 14 400 Punkt, in denen ein PDF misst.
page_origins Array [] Wo das Blatt einer nummerierten Seite in der Zeichnung sitzt: die linke untere Ecke, von der es ausgeht.
page_review_marks Array — Die Formen, die eine Seite über das Gezeichnete legt: eine Notiz, ein Stempel, eine von Hand gezeichnete Form.
page_triggers Array — Was eine Seite auslöst, wenn eine Anzeigesoftware bei ihr ankommt und wenn sie sie verlässt.
document_triggers Objekt — Was das Dokument auslöst, wenn eine Anzeigesoftware es gleich schließen, speichern oder drucken wird, und nachdem sie es gespeichert oder gedruckt hat.
scripts Array [] Die Skripte, die das Dokument unter Namen registriert und die seine Felder und Aktionen aufrufen.
print Zeichenkette — Die Drucknorm, die das Dokument beansprucht und bei deren Verletzung es abgelehnt wird.
variable_print Zeichenkette — Die Norm für variablen Datendruck, die das Dokument beansprucht – eine Datei mit einem Dokument je Empfänger – und bei deren Verletzung es abgelehnt wird.
check Array — Die Normen, an denen das fertige Dokument gemessen wird; die Antwort kommt mit der Datei zurück.
page_spaces Array — Was die nackten Zahlen einer Seite bedeuten, Farbraum für Farbraum, und wie ihre Markierungen zusammengesetzt werden.
page_separations Array — Welche Platte eines separierten Bogens eine Seite ist, und die Druckfarbe, in der sie gedruckt wird.
page_output_intents Array — Das Gerät, für das die Farben einer Seite angegeben sind, wenn Umschlag und Inhalt nicht auf demselben Papier gedruckt werden.
layer_rules Array — Ein Name, auf dem Elemente gezeichnet werden und der für mehrere Ebenen zugleich steht, und die Regel, nach der die Anzeigesoftware entscheidet, ob sie sie zeigt.
layer_configurations Array — Die benannten Sätze von Ebenenzuständen, die die Anzeigesoftware neben dem anbietet, mit dem sich das Dokument öffnet.
layer_families Array — Ebenen, von denen die Anzeigesoftware jeweils eine zeigt, sodass das Einblenden einer die anderen ausblendet.
carried_facts Array [] Benannte Listen eigener Daten, jede an einen Farbabschnitt gehängt von dem Element, das sie nennt. Keine Anzeigesoftware zeigt sie.

Die hundertfünf Seiten dieses Abschnitts

Eine Seite pro Element und pro Schlüssel, die eine Anfrage tragen kann, unter der Frage abgelegt, die sie beantwortet. Jede sagt, was das Ding ist, was es technisch tut, das Schema der Anfrage, die es annimmt, und zeigt eine vollständige Anfrage, die es verwendet.

Was eine Anfrage erzeugt

Fünfundzwanzig Dinge kommen auf eine Seite, und jedes hat hier eine eigene Seite: was es ist, was es technisch tut, das Schema der Anfrage, die es annimmt, und eine vollständige Anfrage, die es zeigt.

Text
Tabellen
Bilder und importierte Seiten
Barcodes und quadratische Codes
Formen
Formularfelder
  • Ein Feld zum Eintippen text_field Fügen Sie ein Feld zum Ausfüllen hinzu: Name, Anschrift, mehrzeiliger Kommentar, verborgenes Passwort oder eine Zahl mit einem Kästchen pro Ziffer.
  • Ein Kästchen zum Ankreuzen check_box Fügen Sie ein Kontrollkästchen hinzu: Bedingungen akzeptieren, eine Option wählen, mit dem Zeichen Ihrer Wahl und dem Wert, den das Formular meldet.
  • Eine Liste zum Auswählen choice_field Bieten Sie eine Liste von Antworten an: eine offene Liste, ein Dropdown-Menü oder ein Menü, in das der Leser auch eine Antwort eintippen kann.
  • Mehrere Schaltflächen, eine Antwort radio_group Platzieren Sie Schaltflächen, von denen jeweils nur eine gewählt bleibt, jede an ihrer eigenen Stelle, alle zur selben Frage gehörig.
  • Der für eine Signatur freigehaltene Platz signature_field Eine Stelle auf der Seite freihalten, an der eine elektronische Signatur Platz nimmt, mit ihrem Namen und ihrem Rahmen.
  • Eine Schaltfläche zum Drücken push_button Platzieren Sie eine Schaltfläche, die etwas auslöst, statt zu antworten: eine Adresse öffnen, zu einer Seite gehen, das Formular senden oder zurücksetzen.
Navigation
  • Eine Stelle der Seite zum Anklicken link Machen Sie eine Stelle der Seite anklickbar: Sie öffnet eine Webadresse oder führt den Leser zu einer anderen Seite desselben Dokuments.

Was eine Anfrage festlegt

Achtzig Schlüssel stehen auf der obersten Ebene einer Anfrage und legen das Dokument um das Gezeichnete herum fest: das Blatt, die Schriften, die Identität der Datei, die Art, wie sich ein Leser durch sie bewegt, die Farbe und die Erzeugung selbst.

Das Blatt und was darunter gelegt wird
Die Schriften
Das Dokument als Ganzes
Sich darin zurechtfinden
Farbe und wie sie gezeigt wird
Was gezeichnet wird, und wie es herauskommt

Die Formen, aus denen jeder Schlüssel gebaut ist

Eine Handvoll Objekte kehrt im ganzen Format immer wieder. Sie werden hier einmal beschrieben, und jede Seite, die eines verwendet, nennt es mit diesen Worten.

Die Objekte, aus denen die JSON-Schlüssel gebaut sind
rect Vier Koordinaten — llx, lly, urx, ury. Der Ursprung liegt links unten auf dem Blatt, und die senkrechte Achse zeigt nach oben; das ist die Konvention des Formats selbst.
Koordinate Eine Zahl oder ein in Anführungszeichen gesetzter Ausdruck über + - * / ( ) und die Variablen {page_width}, {page_height}, {current_page}, {total_pages}, {llx}, {lly}, {urx}, {ury}. Ein Ausdruck lässt sich bis zu hundert Ebenen tief verschachteln; so steht ein Element an der Seitenbreite, ohne dass der Kunde etwas berechnet.
color Eine Zahl für Grau, drei für Rot, Grün und Blau, vier für die vier Druckfarben, alle von 0 bis 1. Außerdem {"gray": n} und {"space": "name", "components": …} für einen von der Anfrage deklarierten Farbraum.
stroke Eine width und optional eine color, ein dash-Muster mit seiner Phase, ein cap und ein join.
pages Die ab eins gezählten Seiten, auf denen ein Element gezeichnet wird. Der Standard, ein leeres Array, zeichnet es auf jeder Seite; so wird eine Fußzeile einmal geschrieben.
layer Die deklarierte Ebene, zu der ein Element gehört; diese schaltet die Anzeigesoftware ein und aus.
id / relative_to Ein Element benennt sich mit id; ein anderes stellt sich mit einem offset an die Kante top oder bottom dieses Elements, sodass ein Absatz einer Tabelle folgt, deren Höhe niemand im Voraus kannte.
transform / rotate Sechs Zahlen für eine Matrix oder eine Gradzahl gegen den Uhrzeigersinn um die Mitte des Rechtecks. Ein Element gibt das eine oder das andere an.

Ein von Ihnen gesendetes PDF ergänzen

POST /update nimmt denselben Body an wie POST /render: die Beschreibung in json_data und das PDF, das ergänzt wird, in einem eigenen Teil. Aus Elementen wird nichts erzeugt. Die Antwort ist die von Ihnen gesendete Datei, jedes Byte an seinem Platz, gefolgt von dem Hinzugefügten: Eine bereits enthaltene Signatur deckt weiterhin ab, was sie abgedeckt hat. x-hqf-pages trägt die Seitenzahl dieser Datei, und danach wird der Aufruf gezählt.

Der Signaturschlüssel verlässt nie Ihre Maschinen, daher wird eine Signatur in zwei Aufrufen versiegelt, und zwischen ihnen wird nichts aufbewahrt. Der erste, prepare_signature, gibt die Datei mit ausgefülltem Signaturfeld zurück, /ByteRange geschrieben und /Contents als Nullen gelassen. Ihre eigene Software signiert die beiden Strecken, die /ByteRange nennt. Der zweite, place_signature, sendet diese Datei mit dem Block zurück, den Ihre Software erstellt hat, und der Block wird in den dafür freigelassenen Platz geschrieben, ohne dass sich ein anderes Byte verschiebt.

  • source — Zeichenkette, erforderlich. Der Name des Teils, der das PDF enthält.
  • expansion_limit — Ganzzahl, optional, eine Anzahl Bytes, höchstens 1 073 741 824. Die maximale Größe, auf die ein Stream des PDF dekomprimiert werden darf. Fehlt sie, gilt die eigene Obergrenze der Bibliothek.
  • answers — Array, optional, standardmäßig leer. Die auszufüllenden Felder, jeweils ein Objekt aus zwei Zeichenketten: field, der Name, unter dem das Formular das Feld meldet, und value, das, was das ausgefüllte Formular meldet — der eingegebene Text, der Zustand, auf den ein Kästchen oder eine Gruppe von Schaltflächen gesetzt ist, Off zum Leeren oder das, was die Option einer Liste meldet.
  • prepare_signature — Objekt, optional. field, eine Zeichenkette, erforderlich, ist das auszufüllende Signaturfeld. reserved_bytes, eine Ganzzahl, erforderlich, höchstens 1 048 576, ist der für den Block freigehaltene Platz. sub_filter ist adbe_pkcs7_detached oder etsi_cades_detached, und digest_method einer der Werte sha256, sha384, sha512, sha3_256, sha3_384, sha3_512 und shake256; fehlt er, gilt der der Bibliothek. name, reason, location und contact sind Zeichenketten, mit denen die Signatur angibt, wer unterschrieben hat, warum, wo und wie die Person erreichbar ist. signed_at ist Datum und Uhrzeit nach ISO 8601. certifies, no_changes, form_filling oder annotating, macht daraus eine Zertifizierung, die so viel erlaubt; fehlt es, ist die Signatur eine Genehmigung.
  • place_signature — Zeichenkette, optional. Der Name des Teils mit dem Block, der in den Platz geschrieben wird, den eine vorbereitete Datei freigelassen hat. Es wird allein angefordert, da der Block die Datei bis zu ihrem letzten Byte abdeckt; answers und prepare_signature können zusammen angefordert werden, wobei die Antworten zuerst geschrieben werden, damit die Signatur sie abdeckt.
  • Mit 400 abgelehnt: eine Beschreibung, die keines der drei verlangt oder einen Block neben etwas anderem platzieren will; ein Name, für den kein Teil gesendet wurde; ein Platz über der Obergrenze; ein Feld, das das Formular nicht enthält, eine Antwort, die es nicht anbietet, ein Signaturfeld, das beantwortet statt signiert wird; ein PDF, das geschützt ist oder keinen vorbereiteten Platz enthält; ein Block, größer als sein Platz. Eine Datei über der Seitenobergrenze wird mit 413 abgelehnt. Das Beantworten eines Formulars und das Vorbereiten einer Signatur erfordern einen Lizenzschlüssel: Ein Evaluierungsserver antwortet mit 402.

Wie eine Anfrage gelesen wird

  • Ein Schlüssel, den der Server nicht kennt, wird ignoriert, sodass eine für eine spätere Version geschriebene Anfrage auch von einer früheren erzeugt wird.
  • Ein zweimal im selben Objekt geschriebener Schlüssel wird abgelehnt, und die Meldung nennt den Schlüssel.
  • Eine Auswahl ohne eigenen Wert wird als bloße Zeichenkette geschrieben — "center", "pdfa4". Eine Auswahl mit Wert ist ein Objekt mit einem einzigen Schlüssel, der die Variante nennt — {"points": 12}, {"page": 3}.
  • Ein fehlender optionaler Schlüssel und ein ausdrückliches null werden gleich gelesen; so kann ein Serialisierer beides ausgeben.
  • Dieselbe Beschreibung, zweimal gesendet, ergibt zweimal dieselben Bytes, außer an den zwei Stellen, an denen das Schema etwas anderes sagt: Ein Abschnitt, der den Zeitpunkt der Erzeugung verlangt, liest die Uhr, während die Seite geschrieben wird, und ein protection-Block ohne eigenen key wird unter zweiunddreißig von der Maschine gelesenen Bytes verschlüsselt. Überall sonst ist die Dateikennung ein Hash des Geschriebenen, sodass sich zwei Erzeugungen als Dateien vergleichen lassen.

Die Zahlen, an die sich der Dienst selbst hält

Jede Zahl und wofür sie eine Obergrenze ist
Der Body einer Anfrage, auf einem eigenen Server 536 870 912 (512 MiB)
Der Body einer Anfrage an den von uns betriebenen Dienst 536 870 912 (512 MiB)
Wie lange der von uns betriebene Dienst auf ein Dokument wartet 30 Sekunden
Wie tief ein Ausdruck verschachtelt ist 100
Einträge in einer indizierten Palette 256
Kompressionsstufe 1 – 9
Fehlerkorrektur eines Aztec-Codes 0 – 90 %
Spalten eines PDF417-Codes 1 – 30
Fehlerkorrekturstufe eines PDF417-Codes 0 – 8
Schriften in einer Kette 2 oder mehr
Hinweise, die eine Antwort trägt 10
Bytes, die diese Hinweise zusammen umfassen 1 000
Seiten der mittleren Vorlage 1
Wie weit sich ein Stream eines mitgelieferten PDF entpackt 1 073 741 824
Zeichen im Namen einer gespeicherten Schrift 64
Seiten in einem Dokument was Ihr Tarif enthält

Die Fragen, die man zuerst stellt

Wird etwas von meinem Dokument aufbewahrt?

Die Seiten nicht. Das fertige PDF geht in der Antwort auf Ihren Aufruf zurück und wird unterwegs nie auf eine Platte geschrieben.

Die Beschreibung, die Sie gesendet haben, und jede mitgesendete Datei werden nur so lange aufbewahrt, wie die Erzeugung dauert, und nur im eigenen Arbeitsspeicher der Maschine. Keines davon wird auf eine Platte geschrieben, und beides ist weg, sobald die Seiten hinausgehen.

Zwei Dinge werden aufgeschrieben, und nur zwei. Das erste sind Schriften. Darf Ihr Konto eigene Schriften senden, wird eine Schrift, die die Erzeugung wirklich verwendet hat, in einem Ordner dieses Kontos aufbewahrt, sodass Sie sie nie ein zweites Mal senden müssen — von da an nennt Ihre Beschreibung sie einfach. Eine gesendete, aber nirgends verwendete Schrift wird nicht aufbewahrt, und eine fehlgeschlagene Erzeugung bewahrt gar nichts auf. Ein Konto, das mit den bereits auf dem Server vorhandenen Schriften zeichnet, bewahrt ebenfalls nichts auf.

Die zweite ist eine Zeile, die ausgelöst wird, wenn der Server auf seiner eigenen Seite ausfällt, damit hier jemand geweckt wird. Sie nennt die Art des Fehlers, das Konto, die Nummer, die Ihre Antwort genannt hat, und den Zeitpunkt, und nichts, was Ihre Beschreibung gewählt hat. Sie wartet in einer kleinen Datei, bis diese Website sie übernommen hat, und verlässt sie, sobald das geschehen ist.

Nichts über die Abrechnung reist vom Server. Diese Website zählt die Erzeugung anhand der ihr zurückgegebenen Antwort und schreibt eine eigene Zeile: den Namen des Kontos, eine Kennung, die für genau diese eine Erzeugung steht, den Zeitpunkt, zu dem sie stattfand, und wie viele Seiten herauskamen. Nichts von dem, was auf diesen Seiten steht, geht mit.

Der Server führt ein Protokoll, wie jedes Programm auf einer Maschine. Er schreibt eine Zeile für jedes herauskommende Dokument, mit dem Namen des Kontos, wie viele Seiten herauskamen und wie lange es dauerte. Er schreibt eine weitere, wenn eine Erzeugung etwas erwähnt, das sie dann nie verwendet hat — eine Schrift, ein Bild, eine Farbe, die nichts ausgewählt hat —, mit dem Namen des Kontos und dem Hinweis. Die Seiten kommen in jedem Fall zurück: Es ist ein Hinweis, keine Ablehnung.

Betreiben Sie den Server auf Ihren eigenen Maschinen, erreicht uns nichts davon. Die Meldung wird eingeschaltet, indem eine Adresse genannt wird, an die gesendet wird, und ein Server, der keine erhalten hat, sendet nirgendwohin etwas.

Wie wird der Server aufgerufen, und in welcher Form?

Eine Adresse nimmt die Arbeit an, und die Anfrage wird als Formular dorthin gesendet. Ein Teil dieses Formulars trägt die Beschreibung des Dokuments, als gewöhnlicher Text geschrieben. Jeder andere Teil ist eine Datei, auf die das Dokument zurückgreift. Was zurückkommt, ist das PDF selbst.

Alles, was ein Formular über das Netz senden kann, kann ihn aufrufen, in welcher Sprache es auch geschrieben ist. Die Tabelle oben nennt Adresse, Form und Antwort genau, und das kurze Beispiel unten ist eine vollständige, funktionierende Anfrage.

Was bringt das gegenüber einer kostenlosen PDF-Bibliothek?

Täglich werden sehr viele Dokumente mit freien PDF-Bibliotheken geschrieben, und mehrere davon sind sehr gut in dem, was sie sich vornehmen. Was hier verkauft wird, ist etwas anderes: ein Stück, das bereits das ganze Gebiet abdeckt, und jemand, der dafür einsteht.

Das Gebiet ist groß. Fließender Text, Tabellen, die über so viele Seiten laufen, wie sie brauchen, Bilder, Druckfarben, Barcodes, auszufüllende Formulare, Dateien, die sich noch in Jahrzehnten öffnen lassen, Dateien, die jemandem vorgelesen werden, der die Seite nicht sehen kann, und der Platz für ein Siegel. Eine Sache zu lernen, eine Sache aktuell zu halten und eine Stelle, an die man schreibt, wenn eine Seite nicht wie erwartet herauskommt.

Es wird von außen geprüft. Es gibt eine kostenlose Prüfsoftware namens veraPDF, deren ganze Aufgabe ist, eine Datei zu öffnen und zu sagen, ob sie den veröffentlichten Regeln folgt, und jedes auf dieser Website gezeigte Archivdokument läuft durch sie. An ihr halten wir keinen Anteil, und sie weiß nichts darüber, wer die Datei vor ihr geschrieben hat.

Dasselbe Dokument wird auf drei Arten geschrieben — ein Rust-Programm, ein Python-Programm und eine Anfrage an den Server —, und die drei werden erneut ausgeführt und verglichen. Wo sie bytegenau gleich herauskommen, steht es daneben; wo sie für das Auge gleich herauskommen, aber nicht bytegenau, steht auch das da.

Und es gibt einen Vertrag, ein Unternehmen und eine Person, an die man schreiben kann. Mehrere freie Teile zusammenzusetzen ist eine gute Arbeitsweise, und es ist auch Arbeit, die jemand tun und weiter tun muss: Wenn zwei davon sich widersprechen, müssen Sie herausfinden, warum. Hier tun wir das.

Die gekaufte Bibliothek gehört Ihnen weiter: einmal bezahlt, so oft installiert, wie Sie möchten, und sie hört nicht an dem Tag auf zu funktionieren, an dem irgendetwas abläuft.

Muss ich etwas installieren?

Bei einem Abonnement nicht. Sie rufen eine Adresse über das Netz auf, und die Seiten kommen zurück. Nichts kommt auf Ihre Maschinen, und nichts muss aktuell gehalten werden.

Kaufen Sie den Server, ist er ein Programm, das Sie auf Ihren eigenen Maschinen starten, mit einer Handvoll Einstellungen: wo seine Schriften liegen, wo sein Lizenzschlüssel liegt und wie viele Seiten ein Dokument umfassen darf. Er lauscht auf der Maschine selbst und steht hinter dem Frontend-Server, den Sie bereits betreiben.

Kaufen Sie die Bibliothek allein, kommt sie wie jeder andere Teil in Ihr Programm, aus Rust oder aus Python. Kein Server, kein Netz, nichts, was daneben läuft.

Wie wird abgerechnet?

Pro Dokument. Jede Erzeugung wird einmal gemeldet, mit der Zahl der erzeugten Seiten, und ein Monat davon wird zu einer Rechnung addiert. Ein Dokument zählt als eines, wie viele Seiten auch daraus hervorgehen: Die Seitenzahl wird neben der Erzeugung festgehalten und nie berechnet.

Ein Bericht, der nicht sofort gesendet werden konnte, geht nicht verloren: Er wartet und geht später. Und ein Bericht, der zweimal ankommt, wird einmal gezählt, denn jede Erzeugung trägt eine eigene Kennung.

Welche Programmiersprachen können sie aufrufen?

Alle. Die Anfrage ist gewöhnlicher Text, der über das Netz gesendet wird; alles, was das kann, kann also ein Dokument anfordern — und das ist jede alltäglich genutzte Sprache.

Die darunterliegende Bibliothek ist in Rust geschrieben und wird aus Python ebenso bereitwillig aufgerufen; damit arbeitet ein Team, das die Bibliothek allein kauft.

Was kann die kostenlose Version?

Sie erzeugt Dokumente, und sie signiert ihre Arbeit. Jede Seite, die sie erzeugt, kommt mit einem Wasserzeichen heraus, und jeder Buchstabe auf diesen Seiten wird als Form nachgezeichnet statt als Text geschrieben, sodass die Seiten richtig aussehen und sich nichts aus ihnen herauslösen lässt.

Einige Dinge erfordern einen Lizenzschlüssel. Eine kostenlose Kopie, die nach einem davon gefragt wird, lehnt ab, sagt das klar und nennt den Lizenzschlüssel, der es abdeckt: das Setzen von Text in einer der vierzehn Schriften, die jede Anzeigesoftware bereits mitbringt, in einer Type-1-Schrift oder in Buchstaben, die das Programm selbst zeichnet, und das Übernehmen einer Seite aus einem anderen Dokument, das mehr als zehntausend eigene Wörter enthält. Eine kostenlose Kopie setzt Text nur in einer Schrift, die eine nachzuzeichnende Glyphe liefert: eine, die Ihre Anfrage mitbringt, oder eine, die der Dienst bereits vorhält.

Welche Regeln werden wirklich geprüft, und von wem?

Dateien, die jahrelang lesbar bleiben sollen, und Dateien, die jemandem vorgelesen werden sollen, der die Seite nicht sehen kann, laufen durch veraPDF, die oben genannte kostenlose Prüfsoftware, und sie akzeptiert sie. Sie wird von der Open Preservation Foundation und der PDF Association betrieben, und an keiner der beiden halten wir einen Anteil.

Für eine Druckmaschine bestimmte Dateien werden an vier Anforderungen gemessen, bevor ein einziges Byte geschrieben ist, und eine Datei, der eine davon fehlt, wird abgelehnt statt geschrieben. Die Seite über Drucknormen nennt die vier.

Woher weiß ich, dass der Dienst antwortet?

Es gibt eine zweite Adresse, deren einzige Aufgabe ist, das zu sagen. Sie erzeugt nichts, die Abfrage kostet nichts, und sie antwortet, solange der Dienst läuft.

Das Zählen Ihrer Dokumente verzögert nie eine Erzeugung. Die Seiten kommen zuerst zurück, und die Zählung läuft danach für sich.

Wohin als Nächstes

Alles, was die Bibliothek auf eine Seite setzt Alles, was der Dienst aufbewahrt, vollständig Was über Sie gespeichert wird, und wie lange Dateien, die sich noch in Jahrzehnten öffnen lassen sollen Dateien, die eine Druckerei ohne Rückfrage druckt

Die Preise ansehen Die Beispiele ansehen