pdfx gebruiken in de terminal en in CI: van het eerste commando tot een betrouwbaar script
Gebruik pdfx op de opdrachtregel om samen te voegen, te comprimeren en watermerken toe te voegen, veel bestanden tegelijk te verwerken en tools met een pipeline in één verzoek te koppelen. Voor scripts en CI: waar u op kunt vertrouwen, namelijk exitcodes, het --json-rapport, standaardinvoer en -uitvoer, en waarom een voorwaardelijke filtertool zonder treffer toch met 0 eindigt.

Onthoud twee dingen voordat u pdfx in een script of in continue integratie (CI) zet. Exitcode 0 betekent niet altijd dat er een bestand is geschreven: een voorwaardelijke filtertool zonder treffer eindigt ook met 0. En --json heeft voor één invoer een andere vorm dan voor meerdere, dus een script dat alleen de array files parset, vindt niets wanneer maar één bestand een treffer was. pdfx is een HTTP-client: bestanden worden voor verwerking naar de server geüpload, er is geen lokale engine en er is geen offlinemodus. De volledige opdrachtreferentie staat in de CLI-gids op de ontwikkelaarspagina.
Installeren, en dan twee bestanden samenvoegen
U hebt Node 20.3 of nieuwer nodig, of Bun:
npm install -g @pdf123/cli
pdfx --version
Wilt u niet globaal installeren, zet dan npx @pdf123/cli voor het commando. Met twee PDF's in de huidige map:
pdfx merge a.pdf b.pdf -o merged.pdf
Bij succes toont de standaarduitvoer merged.pdf. Dat was PDF's samenvoegen. Vraag voordat u van tool wisselt om de velden in plaats van parameternamen te raden:
pdfx describe watermark
Add Watermark (watermark) - Add text or image watermarks to PDF files
Input: 1 file (.pdf)
Result: file
Fields:
--watermarkText <value> Watermark text [default: PDF123]
--fontSize <number> Font size [default: 30]
min 6, max 200
(Een fragment; de velden omvatten ook --rotation, --customColor en andere.) Een veld is gewoon een opdrachtregeloptie:
pdfx watermark in.pdf --watermarkText DRAFT -o marked.pdf
pdfx list toont alle 95 tools, --category security toont alleen één categorie, en met --query watermark zoekt u op woord.
Meerdere bestanden gaan in een map, en bestaande bestanden worden niet overschreven
Geef een tool voor één bestand meerdere invoerbestanden en elk bestand wordt naar de map uit -o geschreven zodra het klaar is, niet pas aan het eind. Mislukt één bestand, dan gaan de andere door en is de exitcode aan het einde van de batch 1.
pdfx compress a.pdf b.pdf -o small/
De standaarduitvoer van één run zag er zo uit, en de volgorde kan telkens verschillen:
a.pdf -> small/a.pdf
b.pdf -> small/b.pdf
Een bestaande map werkt zoals ze is; bestaat de map nog niet, dan heeft -o een slash aan het eind nodig, anders wordt small als bestandsnaam behandeld. Zonder -o komen resultaten in de huidige map onder de naam die de server geeft; een bestaand bestand wordt niet overschreven en het nieuwe wordt a-1.pdf, a-2.pdf. Een specifieke bestandsnaam (-o same.pdf) vervangt bestaande inhoud, zoals u opdroeg. Standaard worden twee bestanden tegelijk verwerkt; wijzig dat met --concurrency. Drukt u halverwege een batch op Ctrl-C, dan blijven de al geschreven bestanden staan en is de exitcode 130.
Open een versleuteld bestand met --input-password PASSWORD. Bevat een batch zowel vergrendelde als niet-vergrendelde bestanden, gebruik dan --password-for FILE=PASSWORD, dat u kunt herhalen.
Roep tools los aan voor tussenbestanden, anders gebruikt u een pipeline
Om samen te voegen, dan een watermerk toe te voegen en dan te comprimeren, vouwt u dit, als u de tussenresultaten niet op schijf nodig hebt, met pipeline samen tot één verzoek; de tussenbestanden worden niet opnieuw gedownload en geüpload. Wilt u het bestand van elke stap, roep de tools dan los aan. Een pipeline levert één bestand op.
pdfx pipeline a.pdf b.pdf --step merge --step compress -o merged-small.pdf
# Als een stap parameters nodig heeft, beschrijf de stappen in JSON
pdfx pipeline a.pdf b.pdf \
--steps '[{"tool":"merge"},{"tool":"watermark","params":{"watermarkText":"DRAFT"}},{"tool":"compress"}]' \
-o out.pdf
Een pipeline heeft maximaal 8 stappen; een 9e stap levert HTTP 400: at most 8 pipeline steps allowed op. Een tool die een tweede bestand nodig heeft (bijvoorbeeld overlay-pdfs, dat een andere PDF eroverheen legt) kan geen pipelinestap zijn. pdfx weigert dat vóór het uploaden, met exitcode 2 en de melding Tool "overlay-pdfs" needs a second file and cannot run as a pipeline step.
Gebruik standaardinvoer alleen als u het commando aan een pipe wilt koppelen. Een bestandsargument - leest van standaardinvoer, en -o - schrijft het resultaat naar standaarduitvoer:
cat report.pdf | pdfx compress - -o - > report-small.pdf
In deze modus bevat de standaarduitvoer alleen de bytes van het bestand; meldingen en fouten gaan naar standaardfout, dus omleiden is veilig. We hebben dit gecontroleerd met een PDF van ongeveer 1 KB: de standaarduitvoer bevatte een PDF van 1040 bytes, even groot als het bestand dat met -o werd geschreven, en de standaardfout was leeg. Leest u van standaardinvoer en geeft u geen -o op, dan krijgt het resultaat de naam stdin.pdf, geschreven naar de huidige map met een opmerking op standaardfout.
Scripts moeten op de reden matchen, niet op de melding
| Exitcode | Betekenis | Voorbeelden |
|---|---|---|
| 0 | Geslaagd, of een voorwaardelijke filtertool zonder treffer | Een samenvoeging slaagde; de voorwaarde van filter-page-count was onwaar |
| 1 | Het verzoek is verstuurd maar mislukt; in een batch is minstens één bestand mislukt | Verkeerd wachtwoord, geen PDF, time-out, niets om terug te geven |
| 2 | Gebruiksfout, er is niets geüpload | Verkeerd gespelde toolnaam, een pipelinestap die een tweede bestand nodig heeft, een resultaattype dat de extensie van het uitvoerbestand tegenspreekt |
| 130 | U hebt het onderbroken | Ctrl-C tijdens een batch |
Bij een fout bevat de standaardfout naast één regel melding nog drie regels: reason:, code: en hint:. Een versleuteld bestand met het verkeerde wachtwoord gaf lokaal dit:
pdfx: HTTP 400: The password is incorrect.
reason: wrong_password
code: bad_request
hint: Check the password and try again.
De manier waarop u het wachtwoord opgeeft, verandert de eerste regel van de melding: unlock --password geeft de regel hierboven. Met --input-password bij een andere tool beschouwt de server het ontgrendelen als een interne stap en luidt de eerste regel HTTP 400: Pipeline step 0 (security/remove-password) failed: The password is incorrect., terwijl de regel reason: in beide gevallen wrong_password is. Zonder wachtwoord is de melding This PDF is password-protected. Enter its password. en is reason gelijk aan password_required. Een script moet matchen op de regel reason:. code is grover, en een veelvoorkomende waarde is bad_request.
Een verkeerd gespelde toolnaam eindigt met 2, en de melding stelt gelijkende namen voor:
pdfx: Unknown tool "compres". Did you mean: compress, decompress-pdf? Run `pdfx list` to see all tools.
Een resultaattype dat de bestandsnaam tegenspreekt eindigt ook met 2, en dat gebeurt voordat er iets wordt geschreven. Het ZIP-bestand dat PDF splitsen oplevert, naar x.pdf schrijven:
pdfx: The result is application/zip but "x.pdf" has a .pdf extension; name it *.zip or write into a directory
code: output_mismatch
Een time-out eindigt ook met 1, met code gelijk aan timeout. De eenheid van --timeout is milliseconden en de standaardwaarde is 300000, dus 5 minuten; --timeout 60 betekent dus 60 milliseconden, geen 60 seconden.
Als de voorwaarde onwaar is, is de exitcode nog steeds 0
Een klasse tools beantwoordt een ja-of-neevraag: is het aantal pagina's groter dan N, bevat het bestand een bepaalde tekst, is het bestand groter dan een bepaalde grootte. Is het antwoord ja, dan geven ze het invoerbestand ongewijzigd terug; is het nee, dan geven ze niets terug, en pdfx toont een regel no match en eindigt met 0. Deze filtertools bestaan alleen in de SDK, de opdrachtregel en MCP; de website heeft er geen pagina voor.
Dit verschilt van een ander soort leeg resultaat. Vindt PDF naar CSV geen tabel in de PDF, dan geeft de server 204 terug en eindigt pdfx met 1 en meldt no_content: de tool moest inhoud opleveren en deed dat niet, en de reden staat in Lege tabelexport (204): uw PDF heeft waarschijnlijk geen kolommen. Voor een filtertool is "geen treffer" het antwoord dat hij hoorde te geven.
Gebruik --json om dit in een script te lezen. Een treffer toont het geschreven bestand; geen treffer toont { "matched": false }:
# Treffer: het bestand wordt geschreven en de info wordt getoond
$ pdfx filter-page-count three.pdf --pageCount 2 --comparator Greater --json -o big/
{ "path": "big/three.pdf", "contentType": "application/pdf", "bytes": 2594 }
# Geen treffer: er wordt geen bestand geschreven
$ pdfx filter-page-count three.pdf --pageCount 5 --comparator Greater --json
{ "matched": false }
Om alleen de bestanden met meer dan 2 pagina's te bewaren, kunt u het zo schrijven. We hebben het lokaal uitgevoerd op a.pdf (1 pagina) en three.pdf (3 pagina's), en alleen de laatste bleef behouden:
mkdir -p big
for f in *.pdf; do
if pdfx filter-page-count "$f" --pageCount 2 --comparator Greater --json -o big/ \
| jq -e '.matched == false' >/dev/null; then
echo "$f: overgeslagen"
fi
done
jq -e '.matched == false' eindigt met 0 als er geen treffer is en met 1 als die er wel is. Bij een treffer heeft pdfx het bestand al in big/ geschreven; de if bepaalt alleen of "overgeslagen" wordt getoond, niet of er wordt geschreven.
Bij één enkele invoer heeft --json geen files-array
Bij meerdere invoerbestanden is de standaarduitvoer onder --json een volledig rapport. input is een absoluut pad. processed telt de bestanden waarvan het verzoek slaagde, inclusief die zonder treffer. unmatched is hoeveel daarvan geen treffer hadden, en hun vermeldingen zijn { "ok": true, "matched": false } zonder path. Heeft elk bestand in een batch geen treffer, dan is de exitcode nog steeds 0. Geeft u --idempotency-key mee aan een batch, dan is de sleutel die voor elk bestand wordt verstuurd <key>:<index>; het mechanisme staat in Idempotency-Key: veilige nieuwe pogingen voor PDF-taken.
{
"processed": 2,
"unmatched": 0,
"failed": 1,
"files": [
{ "input": "/work/a.pdf", "ok": true, "path": "out/a.pdf", "contentType": "application/pdf", "bytes": 1040 },
{ "input": "/work/broken.pdf", "ok": false, "error": "HTTP 400: The file is not a valid PDF or it is damaged.", "reason": "invalid_pdf" },
{ "input": "/work/b.pdf", "ok": true, "path": "out/b.pdf", "contentType": "application/pdf", "bytes": 1027 }
]
}
Bij één invoer wordt het pad voor één bestand gebruikt: --json toont { "path": ..., "contentType": ..., "bytes": ... } en er is geen files-array. Bij een fout is de standaarduitvoer leeg en staat alles op standaardfout. Breidt u bestanden uit met een glob, dan moet het script beide formaten aankunnen, of er nu één bestand een treffer was of meerdere.
Verzamel de bovenstaande regels in één CI-stap
Dit script comprimeert alleen en gebruikt geen filtertool. Een mislukte compressie eindigt met 1, en de stap faalt daarmee. Een filtertool zonder treffer eindigt met 0, zodat CI niet faalt alleen omdat er geen treffer was; of geen treffer een probleem is, bepaalt u zelf door --json te lezen, zoals in de filtersectie hierboven.
Wordt een bestand geweigerd, dan faalt deze stap en worden de bestandsnamen en redenen in het log vermeld. De logica is dezelfde als eerder: lees eerst de exitcode, toon bij een fout de standaardfout, en haal dan met jq de reason uit het batchrapport. Zonder mapargument stopt het meteen, zodat "$1"/*.pdf nooit wordt uitgebreid tot /*.pdf.
#!/usr/bin/env bash
# Gebruik: ./ci-step.sh docs
docs="${1:?gebruik: ./ci-step.sh <map>}"
mkdir -p out
pdfx compress "$docs"/*.pdf -o out/ --json > report.json 2> errors.log
status=$?
if [ "$status" -ne 0 ]; then
cat errors.log >&2
jq -r '.files[]? | select(.ok | not) | "\(.input | split("/") | last)\t\(.reason)"' report.json >&2
fi
exit "$status"
We hebben het lokaal uitgevoerd met vier soorten invoer:
| Bestanden in de map | Exitcode | Log |
|---|---|---|
| Twee goede PDF's | 0 | geen |
| Twee goede PDF's plus een beschadigde | 1 | pdfx: broken.pdf: HTTP 400: ..., dan een regel broken.pdf invalid_pdf |
| Eén goede PDF | 0 | geen; report.json heeft het formaat voor één bestand |
| Eén beschadigde PDF | 1 | reason: invalid_pdf, code: invalid_document en een regel hint; report.json is leeg |
Het vraagteken aan het eind van .files[]? in jq voorkomt dat het rapport voor één bestand (dat geen files heeft) een fout veroorzaakt. Eén beschadigd bestand heeft geen batchrapport en de reden staat alleen in errors.log, dus bewaar beide uitvoerregels.
Nog drie dingen om te controleren voordat u dit in CI zet. pdfx heeft het netwerk nodig: bestanden worden geüpload naar PDFX_API_BASE, dat standaard https://pdf123.xyz is. Voor documenten die binnen uw netwerk moeten blijven, draait u zelf een service en wijst u deze variabele ernaar; zie Wat self-hosting u echt oplevert (en wat het kost). Hebt u een identiteit nodig, zet de sleutel dan in PDFX_API_KEY en niet in opdrachtregelargumenten, waar hij in de proceslijst en de logs blijft staan. De nu gepubliceerde versie is 0.1.0; gebruik in CI npx @pdf123/[email protected] ... om die vast te zetten, zodat het uitvoerformaat en de exitcodes niet met nieuwe releases meeveranderen en een upgrade een wijziging wordt die u bewust doet.
De pagina van het pakket op npm is @pdf123/cli.