How-to2026-09-2910 мин четене

pdfx в терминала и в CI: от първата команда до надежден скрипт

Използвайте pdfx в командния ред, за да обединявате, компресирате и слагате водни знаци, да обработвате много файлове наведнъж и да свързвате инструменти в една заявка с pipeline. За скриптове и CI научете на какво да разчитате: кодове за изход, отчета от --json, стандартния вход и изход и защо условен филтриращ инструмент без съвпадение пак излиза с 0.

PDF123 · Updated 2026-09-29

Преди да сложите pdfx в скрипт или в непрекъсната интеграция (CI), помнете две неща. Код за изход 0 не винаги означава, че е записан файл: условен филтриращ инструмент без съвпадение също излиза с 0. А --json има различна форма при един вход и при няколко, така че скрипт, който разчита само масива files, не намира нищо, когато е съвпаднал само един файл. pdfx е HTTP клиент: файловете се качват на сървъра за обработка, няма локален двигател и няма офлайн режим. Пълният справочник на командите е в ръководството за CLI на страницата за разработчици.

Диаграма: едно извикване на pdfx дава на скрипта три неща, JSON отчет на стандартния изход, съобщения за неуспех на стандартния изход за грешки и код за изход; скриптът чете първо кода за изход

Инсталирайте го, после обединете два файла

Трябва ви Node 20.3 или по-нов, или Bun:

npm install -g @pdf123/cli
pdfx --version

Ако не искате глобална инсталация, поставете npx @pdf123/cli пред командата. С два PDF в текущата папка:

pdfx merge a.pdf b.pdf -o merged.pdf

При успех стандартният изход печата merged.pdf. Това беше Обединяване на PDF. Преди да смените инструмента, поискайте полетата, вместо да гадаете имена на параметри:

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

(Откъс; полетата включват още --rotation, --customColor и други.) Едно поле е просто опция на командния ред:

pdfx watermark in.pdf --watermarkText DRAFT -o marked.pdf

pdfx list показва всичките 95 инструмента, --category security показва само една категория, а --query watermark търси по дума.

Няколко файла отиват в папка, а съществуващите файлове не се презаписват

Подайте на инструмент за един файл няколко входа и всеки файл се записва в папката, посочена с -o, веднага щом приключи, без да се чака края. Ако един файл пропадне, останалите продължават, а кодът за изход в края на партидата е 1.

pdfx compress a.pdf b.pdf -o small/

Стандартният изход от едно пускане изглеждаше така, а редът може да е различен всеки път:

a.pdf -> small/a.pdf
b.pdf -> small/b.pdf

Съществуваща папка работи такава, каквато е; ако папката още не съществува, -o се нуждае от завършващо /, иначе small се приема за име на файл. Без -o резултатите попадат в текущата папка под името, което дава сървърът; съществуващ файл не се презаписва и новият става a-1.pdf, a-2.pdf. Конкретно име на файл (-o same.pdf) замества съществуващото съдържание, както сте поискали. По подразбиране се обработват по два файла едновременно; променя се с --concurrency. Ако натиснете Ctrl-C по средата на партида, вече записаните файлове остават, а кодът за изход е 130.

Отворете шифрован файл с --input-password PASSWORD. Когато партидата смесва заключени и отключени файлове, ползвайте --password-for FILE=PASSWORD, който може да се повтаря.

Викайте инструментите поотделно за междинни файлове, иначе ползвайте pipeline

За да обедините, после да добавите воден знак, после да компресирате, когато не ви трябват междинните резултати на диска, съберете ги в една заявка с pipeline; междинните файлове не се теглят и качват наново. Ако искате файла от всяка стъпка, викайте инструментите поотделно. Конвейерът произвежда един файл.

pdfx pipeline a.pdf b.pdf --step merge --step compress -o merged-small.pdf

# When a step needs parameters, describe the steps in JSON
pdfx pipeline a.pdf b.pdf \
  --steps '[{"tool":"merge"},{"tool":"watermark","params":{"watermarkText":"DRAFT"}},{"tool":"compress"}]' \
  -o out.pdf

Конвейерът има най-много 8 стъпки; 9-ата стъпка получава HTTP 400: at most 8 pipeline steps allowed. Инструмент, който иска втори файл (например overlay-pdfs, който налага друг PDF отгоре), не може да е стъпка от конвейер. pdfx го отхвърля преди качването, с код за изход 2 и съобщението Tool "overlay-pdfs" needs a second file and cannot run as a pipeline step.

Ползвайте стандартния вход само когато искате да закачите командата към канал. Аргумент - чете от стандартния вход, а -o - пише резултата на стандартния изход:

cat report.pdf | pdfx compress - -o - > report-small.pdf

В този режим стандартният изход носи само байтовете на файла; съобщенията и грешките отиват на стандартния изход за грешки, така че пренасочването е безопасно. Проверихме го с PDF от около 1 KB: стандартният изход съдържаше PDF от 1040 байта, със същия размер като файла, записан с -o, а стандартният изход за грешки беше празен. Когато четете от стандартния вход и не подавате -o, резултатът се нарича stdin.pdf, записва се в текущата папка и на стандартния изход за грешки се печата бележка.

Скриптовете да следват причината, а не съобщението

Код за изход Значение Примери
0 Успех или условен филтриращ инструмент без съвпадение Обединяването успя; условието на filter-page-count беше невярно
1 Заявката е изпратена, но е пропаднала; в партида е пропаднал поне един файл Грешна парола, не е PDF, изтекло време, няма какво да се върне
2 Грешка в употребата, нищо не е качено Сгрешено име на инструмент, стъпка от конвейер, която иска втори файл, тип на резултата, който противоречи на разширението на изходния файл
130 Вие го прекъснахте Ctrl-C по време на партида

При неуспех, освен един ред със съобщение, стандартният изход за грешки носи три реда: reason:, code: и hint:. Шифрован файл с грешна парола даде това локално:

pdfx: HTTP 400: The password is incorrect.
reason: wrong_password
code: bad_request
hint: Check the password and try again.

Начинът, по който подавате паролата, променя първия ред на съобщението: unlock --password дава реда по-горе. С --input-password при друг инструмент сървърът третира отключването като вътрешна стъпка и първият ред е HTTP 400: Pipeline step 0 (security/remove-password) failed: The password is incorrect., докато редът reason: е wrong_password и в двата случая. Без парола изобщо съобщението е This PDF is password-protected. Enter its password., а reason е password_required. Скриптът трябва да съпоставя реда reason:. code е по-груб, а честа стойност е bad_request.

Сгрешено име на инструмент излиза с 2, а съобщението предлага подобни имена:

pdfx: Unknown tool "compres". Did you mean: compress, decompress-pdf? Run `pdfx list` to see all tools.

Тип на резултата, който противоречи на името на файла, също излиза с 2 и това става, преди да е записано каквото и да е. Записване на ZIP файла, който произвежда Разделяне на PDF, в x.pdf:

pdfx: The result is application/zip but "x.pdf" has a .pdf extension; name it *.zip or write into a directory
code: output_mismatch

Изтичането на времето също излиза с 1, а code е timeout. Единицата на --timeout са милисекунди, а стойността по подразбиране е 300000, тоест 5 минути, така че --timeout 60 означава 60 милисекунди, не 60 секунди.

Когато условието е невярно, кодът за изход пак е 0

Един клас инструменти отговарят на въпрос с да или не: дали броят страници е по-голям от N, дали файлът съдържа определен текст, дали файлът е по-голям от някакъв размер. Когато отговорът е да, връщат входния файл непроменен; когато е не, не връщат нищо, а pdfx печата ред no match и излиза с 0. Тези филтриращи инструменти съществуват само в SDK, командния ред и MCP; сайтът няма страница за тях.

Това се различава от друг вид празен резултат. Когато PDF към CSV не намери таблица в PDF, сървърът връща 204, а pdfx излиза с 1 и съобщава no_content: инструментът е трябвало да произведе съдържание и не е, а причината е в Празен експорт на таблица (204): PDF-ът ви вероятно няма колони. За филтриращия инструмент „няма съвпадение“ е отговорът, който той е трябвало да даде.

За да го прочетете в скрипт, ползвайте --json. Съвпадение отпечатва записания файл; липса на съвпадение отпечатва { "matched": false }:

# Match: the file is written, and its info is printed
$ pdfx filter-page-count three.pdf --pageCount 2 --comparator Greater --json -o big/
{ "path": "big/three.pdf", "contentType": "application/pdf", "bytes": 2594 }

# No match: no file is written
$ pdfx filter-page-count three.pdf --pageCount 5 --comparator Greater --json
{ "matched": false }

За да запазите само файловете с повече от 2 страници, можете да го напишете така. Пуснахме го локално върху a.pdf (1 страница) и three.pdf (3 страници) и остана само вторият:

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: пропуснат"
  fi
done

jq -e '.matched == false' излиза с 0, когато няма съвпадение, и с 1, когато има. При съвпадение файлът вече е записан в big/ от pdfx; if решава само дали да се отпечата „пропуснат“, а не дали да се записва.

При един-единствен вход --json няма масив files

При няколко входа стандартният изход под --json е пълен отчет. input е абсолютен път. processed брои файловете, чиято заявка е успяла, включително тези без съвпадение. unmatched е колко от тях не са имали съвпадение, а записите им са { "ok": true, "matched": false } без path. Когато във всеки файл от партидата няма съвпадение, кодът за изход пак е 0. Когато подадете --idempotency-key на партида, ключът, изпращан за всеки файл, е <key>:<index>; механизмът е в Idempotency-Key: безопасни повторни опити за PDF задачи.

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

При един вход се минава по пътя за един файл: --json печата { "path": ..., "contentType": ..., "bytes": ... } и няма масив files. При неуспех стандартният изход е празен и всичко е на стандартния изход за грешки. Когато разгръщате файлове с glob, скриптът трябва да се справя с двата формата, независимо дали е съвпаднал един файл, или няколко.

Съберете горните правила в една стъпка за CI

Този скрипт само компресира и не използва филтриращ инструмент. Неуспехът на компресирането излиза с 1 и стъпката пропада с него. Филтриращ инструмент без съвпадение излиза с 0, така че CI не пропада само защото няма съвпадение; дали липсата на съвпадение е проблем, решавате вие, като четете --json, както в раздела за филтрите по-горе.

Ако някой файл бъде отхвърлен, тази стъпка пропада и изброява в лога имената на файловете и причините. Логиката е същата като преди: първо четете кода за изход, при неуспех отпечатайте стандартния изход за грешки, после с jq извадете reason от отчета на партидата. Без аргумент за папка излиза веднага, така че "$1"/*.pdf никога не се разгръща до /*.pdf.

#!/usr/bin/env bash
# Употреба: ./ci-step.sh docs
docs="${1:?употреба: ./ci-step.sh <папка>}"
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"

Пуснахме го локално с четири вида вход:

Файлове в папката Код за изход Лог
Два читави PDF 0 няма
Два читави PDF плюс един повреден 1 pdfx: broken.pdf: HTTP 400: ..., после ред broken.pdf invalid_pdf
Един читав PDF 0 няма; report.json е във формата за един файл
Един повреден PDF 1 reason: invalid_pdf, code: invalid_document и ред hint; report.json е празен

Въпросителният знак в края на .files[]? в jq не позволява отчетът за един файл (който няма files) да предизвика грешка. Един повреден файл няма отчет за партида и причината се появява само в errors.log, така че пазете и двата реда изход.

Още три неща трябва да проверите, преди да сложите това в CI. pdfx се нуждае от мрежа: файловете се качват на PDFX_API_BASE, по подразбиране https://pdf123.xyz. За документи, които трябва да останат във вашата мрежа, пуснете собствена услуга и насочете тази променлива към нея; вижте Какво всъщност ви дава самостоятелното хостване (и какво струва). Когато ви трябва самоличност, сложете ключа в PDFX_API_KEY, а не в аргументите на командния ред, където остава в списъка с процеси и в логовете. Публикуваната в момента версия е 0.1.0; в CI ползвайте npx @pdf123/[email protected] ..., за да я закрепите, така че форматът на изхода и кодовете за изход да не се менят с новите издания, а надграждането да е промяна, която правите нарочно.

Страницата на пакета в npm е @pdf123/cli.

Open tool
Process in the browser — no watermark, files removed after the job.
Open tool
Read next
Номерация на страниците в PDF: позиция, начален номер и клопката със завъртяните страници
Как да номерирате страниците на съществуващ PDF онлайн: изберете ъгъл, задайте начален номер като 101 и избегнете номерата настрани, които се появяват на страници, записани като завъртени.
Конвертиране на PDF в JPG или PNG: какво променя DPI и как да експортирате една страница
PDF към изображения рендира всяка страница в ZIP, като PNG, освен ако изберете JPEG. Браузърната форма има само формат и DPI; API полетата за избор на страници и за WebP се игнорират. Измерени размери на файловете при 150 и 300 DPI и как да експортирате една страница.
Снимки от телефона към PDF: защо файлът излиза огромен и страница се обръща настрани
Изображения към PDF поставя всяка снимка на отделна страница в пълния му размер в пиксели, затова три JPEG от по 3,2 MB от телефон станаха 41,5 MB PDF, а портретна снимка излезе на пейзажна страница. Смалете снимките преди конвертиране и завъртете страницата настрани след това.