Terminalde ve CI’da pdfx Kullanımı: İlk Komuttan Güvenilir Bir Betiğe
Birleştirmek, sıkıştırmak ve filigran eklemek için pdfx’i komut satırında kullanın, birçok dosyayı bir seferde işleyin ve araçları pipeline ile tek bir isteğe zincirleyin. Betikler ve CI için nelere güvenileceğini öğrenin: çıkış kodları, --json raporu, standart girdi ve çıktı ve eşleşme bulamayan koşullu bir filtre aracının neden yine de 0 ile çıktığı.

pdfx’i bir betiğe ya da sürekli entegrasyona (CI) koymadan önce iki şeyi aklınızda tutun. Çıkış kodu 0, her zaman bir dosyanın yazıldığı anlamına gelmez: eşleşme bulamayan koşullu bir filtre aracı da 0 ile çıkar. Ayrıca --json’un biçimi tek girdide çoklu girdiden farklıdır; yalnızca files dizisini ayrıştıran bir betik, tek bir dosya eşleştiğinde hiçbir şey bulamaz. pdfx bir HTTP istemcisidir: dosyalar işlenmek üzere sunucuya yüklenir, yerel bir motor yoktur ve çevrimdışı mod yoktur. Tam komut başvurusu geliştirici sayfasındaki CLI kılavuzunda yer alır.
Kurun, sonra iki dosyayı birleştirin
Node 20.3 veya daha yenisi ya da Bun gerekir:
npm install -g @pdf123/cli
pdfx --version
Genel olarak kurmak istemiyorsanız komutun önüne npx @pdf123/cli koyun. Geçerli dizinde iki PDF varken:
pdfx merge a.pdf b.pdf -o merged.pdf
Başarılı olursa standart çıktı merged.pdf yazdırır. Bu, PDF birleştir aracıydı. Başka bir araca geçmeden önce parametre adlarını tahmin etmek yerine alanları isteyin:
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
(Bir alıntıdır; alanlar arasında --rotation, --customColor ve başkaları da vardır.) Bir alan, sıradan bir komut satırı seçeneğidir:
pdfx watermark in.pdf --watermarkText DRAFT -o marked.pdf
pdfx list 95 aracın tamamını listeler, --category security yalnızca tek bir kategoriyi listeler ve --query watermark sözcükle arar.
Birden çok dosya bir dizine gider ve mevcut dosyaların üzerine yazılmaz
Tek dosyalı bir araca birden çok girdi verin; her dosya biter bitmez, sona kadar tutulmadan, -o ile adlandırılan dizine yazılır. Bir dosya başarısız olursa diğerleri sürer ve grubun sonundaki çıkış kodu 1 olur.
pdfx compress a.pdf b.pdf -o small/
Bir çalıştırmanın standart çıktısı şöyle göründü; sıra her seferinde değişebilir:
a.pdf -> small/a.pdf
b.pdf -> small/b.pdf
Var olan bir dizin olduğu gibi çalışır; dizin henüz yoksa -o sonunda / gerektirir; aksi hâlde small bir dosya adı sayılır. -o verilmezse sonuçlar, sunucunun verdiği adla geçerli dizine düşer; var olan bir dosyanın üzerine yazılmaz ve yeni dosya a-1.pdf, a-2.pdf olur. Belirli bir dosya adı (-o same.pdf), söylediğiniz gibi mevcut içeriğin yerine geçer. Varsayılan olarak aynı anda iki dosya işlenir; bunu --concurrency ile değiştirebilirsiniz. Bir grubun ortasında Ctrl-C’ye basarsanız yazılmış dosyalar kalır ve çıkış kodu 130 olur.
Şifreli bir dosyayı --input-password PASSWORD ile açın. Bir grup kilitli ve kilitsiz dosyaları karışık içeriyorsa, tekrarlayabileceğiniz --password-for FILE=PASSWORD kullanın.
Ara dosyalar için araçları ayrı çağırın, yoksa pipeline kullanın
Birleştirip, sonra filigran ekleyip, sonra sıkıştırmak için ara sonuçları diskte istemiyorsanız, bunları pipeline ile tek bir isteğe katlayın; ara dosyalar yeniden indirilip yüklenmez. Her adımın dosyasını istiyorsanız araçları ayrı ayrı çağırın. Bir pipeline tek bir dosya üretir.
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
Bir pipeline en fazla 8 adım içerir; 9. adım HTTP 400: at most 8 pipeline steps allowed hatasını alır. İkinci bir dosya gerektiren bir araç (örneğin başka bir PDF’yi üzerine bindiren overlay-pdfs) pipeline adımı olamaz. pdfx onu yüklemeden önce reddeder; çıkış kodu 2 ve ileti Tool "overlay-pdfs" needs a second file and cannot run as a pipeline step olur.
Standart girdiyi yalnızca komutu bir boru hattına bağlamak istediğinizde kullanın. - dosya argümanı standart girdiden okur ve -o - sonucu standart çıktıya yazar:
cat report.pdf | pdfx compress - -o - > report-small.pdf
Bu kipte standart çıktı yalnızca dosyanın baytlarını taşır; iletiler ve hatalar standart hataya gider, bu yüzden yönlendirme güvenlidir. Bunu yaklaşık 1 KB’lık bir PDF ile denedik: standart çıktı 1040 baytlık bir PDF taşıdı, -o ile yazılan dosyayla aynı boyuttaydı ve standart hata boştu. Standart girdiden okuyup -o vermezseniz sonuç stdin.pdf olarak adlandırılır, geçerli dizine yazılır ve standart hataya bir not düşülür.
Betikler iletiyle değil, nedenle eşleşmeli
| Çıkış kodu | Anlamı | Örnekler |
|---|---|---|
| 0 | Başarı ya da eşleşme bulamayan koşullu bir filtre aracı | Birleştirme başarılı oldu; filter-page-count koşulu yanlıştı |
| 1 | İstek gönderildi ama başarısız oldu; bir grupta en az bir dosya başarısız oldu | Yanlış parola, PDF değil, zaman aşımı, döndürülecek bir şey yok |
| 2 | Kullanım hatası, hiçbir şey yüklenmedi | Yanlış yazılmış araç adı, ikinci dosya gerektiren bir pipeline adımı, çıktı dosyasının uzantısıyla çelişen bir sonuç türü |
| 130 | Siz yarıda kestiniz | Bir grup sırasında Ctrl-C |
Başarısızlıkta, bir satırlık iletinin yanı sıra standart hata üç satır daha taşır: reason:, code: ve hint:. Yanlış parolalı şifreli bir dosya bunu yerelde üretti:
pdfx: HTTP 400: The password is incorrect.
reason: wrong_password
code: bad_request
hint: Check the password and try again.
Parolayı verme biçiniz iletinin ilk satırını değiştirir: unlock --password yukarıdaki satırı verir. Başka bir araçta --input-password ile sunucu kilit açmayı iç bir adım sayar ve ilk satır HTTP 400: Pipeline step 0 (security/remove-password) failed: The password is incorrect. olur; reason: satırı ise her iki durumda da wrong_password’dür. Hiç parola verilmezse ileti This PDF is password-protected. Enter its password. ve reason password_required olur. Bir betik reason: satırıyla eşleşmelidir. code daha kabadır ve yaygın bir değeri bad_request’tir.
Yanlış yazılmış bir araç adı 2 ile çıkar ve ileti benzer adlar önerir:
pdfx: Unknown tool "compres". Did you mean: compress, decompress-pdf? Run `pdfx list` to see all tools.
Dosya adıyla çelişen bir sonuç türü de 2 ile çıkar ve bu, hiçbir şey yazılmadan önce olur. PDF böl aracının ürettiği ZIP’i x.pdf dosyasına yazmak:
pdfx: The result is application/zip but "x.pdf" has a .pdf extension; name it *.zip or write into a directory
code: output_mismatch
Zaman aşımı da 1 ile çıkar ve code değeri timeout olur. --timeout birimi milisaniyedir ve varsayılan 300000’dir, yani 5 dakikadır; bu yüzden --timeout 60 60 saniye değil 60 milisaniye demektir.
Koşul yanlış olduğunda çıkış kodu yine 0’dır
Bir araç sınıfı evet ya da hayır sorusunu yanıtlar: sayfa sayısı N’den büyük mü, dosya belirli bir metni içeriyor mu, dosya belirli bir boyuttan büyük mü. Yanıt evetse girdi dosyasını değiştirmeden döndürürler; hayırsa hiçbir şey döndürmezler ve pdfx bir no match satırı yazdırıp 0 ile çıkar. Bu filtre araçları yalnızca SDK’da, komut satırında ve MCP’de vardır; sitede bunlar için bir sayfa yoktur.
Bu, başka türden bir boş sonuçtan farklıdır. PDF'den CSV'ye PDF’de tablo bulamazsa sunucu 204 döndürür, pdfx 1 ile çıkar ve no_content bildirir: araç içerik üretmeyi amaçlamıştı ve üretemedi; nedeni Boş tablo dışa aktarma (204): PDF’nizde büyük olasılıkla sütun yok yazısında anlatılır. Bir filtre aracı için "eşleşme yok" vermesi gereken yanıtın kendisidir.
Bunu bir betikte okumak için --json kullanın. Eşleşme yazılan dosyayı yazdırır; eşleşme yoksa { "matched": false } yazdırılır:
# 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 }
Yalnızca 2 sayfadan fazla olan dosyaları tutmak için şöyle yazabilirsiniz. Bunu yerelde a.pdf (1 sayfa) ve three.pdf (3 sayfa) üzerinde çalıştırdık ve yalnızca ikincisi tutuldu:
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: atlandı"
fi
done
jq -e '.matched == false', eşleşme yokken 0, eşleşme varken 1 ile çıkar. Eşleşme olduğunda dosya pdfx tarafından zaten big/ içine yazılmıştır; if yalnızca "atlandı" yazdırılıp yazdırılmayacağına karar verir, yazılıp yazılmayacağına değil.
Tek bir girdi olduğunda --json’da files dizisi yoktur
Birden çok girdide --json altındaki standart çıktı tam bir rapordur. input mutlak bir yoldur. processed, isteği başarılı olan dosyaları sayar; eşleşmeyenler de dâhildir. unmatched, bunlardan kaçının eşleşmediğidir ve girdileri path içermeyen { "ok": true, "matched": false } biçimindedir. Bir gruptaki her dosya eşleşmediğinde çıkış kodu yine 0’dır. Bir gruba --idempotency-key verdiğinizde her dosya için gönderilen anahtar <key>:<index> olur; mekanizma Idempotency-Key: PDF işleri için güvenli yeniden denemeler yazısındadır.
{
"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 }
]
}
Tek bir girdide tek dosyalı yol kullanılır: --json { "path": ..., "contentType": ..., "bytes": ... } yazdırır ve files dizisi yoktur. Başarısızlıkta standart çıktı boştur ve her şey standart hatadadır. Dosyaları bir glob ile genişlettiğinizde, bir dosya da eşleşse birden çok da, betiğin her iki biçimi de ele alması gerekir.
Yukarıdaki kuralları tek bir CI adımında toplayın
Bu betik yalnızca sıkıştırır ve hiçbir filtre aracı kullanmaz. Sıkıştırma hatası 1 ile çıkar ve adım da onunla birlikte başarısız olur. Eşleşme bulamayan bir filtre aracı 0 ile çıkar; böylece CI yalnızca eşleşme olmadığı için başarısız olmaz. Eşleşmemenin sorun sayılıp sayılmayacağına, yukarıdaki filtre bölümünde olduğu gibi --json çıktısını okuyarak siz karar verirsiniz.
Herhangi bir dosya reddedilirse bu adım başarısız olur ve günlükte dosya adlarını ve nedenlerini listeler. Mantık öncekiyle aynıdır: önce çıkış kodunu okuyun, başarısızlıkta standart hatayı yazdırın, sonra jq ile reason değerini grup raporundan çekin. Dizin argümanı olmadan hemen çıkar; böylece "$1"/*.pdf hiçbir zaman /*.pdf olarak genişletilmez.
#!/usr/bin/env bash
# Kullanım: ./ci-step.sh docs
docs="${1:?kullanım: ./ci-step.sh <dizin>}"
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"
Bunu dört tür girdiyle yerelde çalıştırdık:
| Dizindeki dosyalar | Çıkış kodu | Günlük |
|---|---|---|
| İki sağlam PDF | 0 | yok |
| İki sağlam PDF artı hasarlı bir PDF | 1 | pdfx: broken.pdf: HTTP 400: ..., then a line broken.pdf invalid_pdf |
| Tek bir sağlam PDF | 0 | yok; report.json tek dosyalı biçimdedir |
| Tek bir hasarlı PDF | 1 | reason: invalid_pdf, code: invalid_document and a hint line; report.json is empty |
jq içindeki .files[]? sonundaki soru işareti, tek dosyalı raporun (files içermeyen) hata vermesini önler. Tek bir hasarlı dosyanın grup raporu yoktur ve neden yalnızca errors.log içinde görünür; bu yüzden iki çıktı dosyasını da saklayın.
Bunu CI’a koymadan önce kontrol etmeniz gereken üç şey daha var. pdfx ağa ihtiyaç duyar: dosyalar PDFX_API_BASE adresine yüklenir; varsayılanı https://pdf123.xyz’dir. Ağınızın içinde kalması gereken belgeler için kendi servisinizi çalıştırın ve bu değişkeni ona yöneltin; bkz. Kendi sunucunuzda barındırmak size gerçekte ne kazandırır (ve neye mal olur). Bir kimliğe ihtiyaç duyduğunuzda anahtarı komut satırı argümanlarına değil PDFX_API_KEY içine koyun; argümanlar süreç listesinde ve günlüklerde kalır. Şu anda yayımlanan sürüm 0.1.0’dır; CI’da sabitlemek için npx @pdf123/[email protected] ... kullanın, böylece çıktı biçimi ve çıkış kodları yeni sürümlerle değişmez ve bir yükseltme bilerek yaptığınız bir değişikliğe dönüşür.
Paketin npm üzerindeki sayfası @pdf123/cli adresindedir.