use path use re use str fn is-pdf {|file| eq (path:ext $file) '.pdf' } fn exist {|file| or (path:is-dir &follow-symlink=$true $file) (path:is-regular &follow-symlink=$true $file) } fn not-exist {|file| not (exist $file) } fn is-pdf-exist {|file| and (is-pdf $file) (exist $file) } fn is-number {|v| re:match '^\d+$' (to-string $v) } fn is-page-selection {|v| re:match '^(r?\d+|z)(-(r?\d+|z))?(,(r?\d+|z)(-(r?\d+|z))?)*(:(even|odd))?$' (to-string $v) } fn is-rotate-selection {|v| re:match '^(\+|-)?\d+(:(r?\d+|z)(-(r?\d+|z))?(,(r?\d+|z)(-(r?\d+|z))?)*(:(even|odd))?)?$' (to-string $v) } fn is-object {|v| re:match '^ \d+\ 0\ R$' $v } fn is-selection {|v| or (is-page-selection $v) (is-rotate-selection $v) } fn version {|in| var version = (head -n 1 $in) set version @_ = (str:split "\r" $version) str:trim-prefix $version '%PDF-' } fn json {|in| qpdf $in --json | from-json } fn form {|in| var json = (json $in) put $json[acroform] } fn encryption {|in| var json = (json $in) put $json[encrypt] } fn objects {|in| var json = (json $in) put $json[objects] } fn -format {|dict o| var t = (kind-of $o) if (eq $t string) { if (has-key $dict $o) { set o = $dict[$o] } } elif (eq $t list) { set @o = (all $o | each {|e| if (has-key $dict $e) { put $dict[$e] } else { put $e } }) } elif (eq $t map) { keys $o | each {|k| set o[$k] = (-format $dict $o[$k]) } } put $o } fn -deep {|&dict=$nil objects @keys| if (== (count $keys) 0) { if (not-eq $dict $nil) { put (-format $dict $objects) $true } else { put $objects $true } return } if (not-eq $dict $nil) { set objects = (-format $dict $objects) } var id @next = $@keys var t = (kind-of $objects) if (eq $t map) { if (has-key $objects $id) { -deep &dict=$dict $objects[$id] $@next } } elif (eq $t list) { if (and (is-number $id) (< $id (count $objects))) { -deep &dict=$dict $objects[$id] $@next } } else { put $nil $false } } fn -object {|&extend=$false objects id| var dict = $nil if $extend { set dict = $objects } var o _ = (-deep &dict=$dict $objects $id) put $o } fn deep {|&extend=$false in @keys| var dict = (objects $in) if $extend { -deep &dict=$dict $dict $@keys } else { -deep $dict $@keys } } fn contains {|in @keys| var _ ok = (deep $in $@keys) put $ok } fn value {|&extend=$false in @keys| var v _ = (deep &extend=$extend $in $@keys) put $v } fn object {|&extend=$false in key| -object &extend=$extend (objects $in) $key } fn -filter {|&extend=$false objects cond| var out = [&] keys $objects | each {|k| var o = (-object &extend=$extend $objects $k) if ($cond $o) { set out[$k] = $o } } put $out } fn filter {|&extend=$false in cond| -filter &extend=$extend (objects $in) $cond } fn font-filter {|o| and ^ (eq (kind-of $o) map) ^ (has-key $o /Type) ^ (eq $o[/Type] /Font) } fn fonts {|in| var dict = (objects $in) var fonts = (-filter $dict $font-filter~) keys $fonts | each {|id| var f = $fonts[$id] keys $f | each {|k| var v = $f[$k] if (has-key $dict $v) { set f[$k] = $dict[$v] } } var fd = $f[/FontDescriptor] keys $fd | each {|k| var v = $fd[$k] if (and (not-eq $k /FontFile2) (has-key $dict $v)) { set fd[$k] = $dict[$v] } } set f[/FontDescriptor] = $fd set f[id] = $id put $f } } fn image-filter {|o| and ^ (eq (kind-of $o) map) ^ (has-key $o /Subtype) ^ (eq $o[/Subtype] /Image) } fn images {|in| var json = (json $in) var dict = $json[objects] all $json[pages] | each {|p| var n = $p[pageposfrom1] all $p[images] | each {|i| var id = $i[object] var img = (-object $dict $id) keys $img | each {|k| var v = $img[$k] if (has-key $dict $v) { set img[$k] = $dict[$v] } } if (has-key $img /ColorSpace) { var @cs = (all $img[/ColorSpace] | each {|v| if (has-key $dict $v) { put $dict[$v] } else { put $v } }) set img[/ColorSpace] = $cs } if (has-key $img /DecodeParms) { var dp = $img[/DecodeParms] keys $dp | each {|k| var v = $dp[$k] if (has-key $dict $v) { set dp[$k] = $dict[$v] } } set img[/DecodeParms] = $dp } set img[id] = $id set img[page] = $n set img[name] = $i[name] put $img } } } fn trailer {|&extend=$false in| object &extend=$extend $in 'trailer' } fn pages {|in| var json = (json $in) put $json[pages] } fn page {|in nb| put (pages $in)[(- $nb 1)] } fn nb-pages {|in| qpdf --show-npages $in } fn attachments {|in| var data = [] var json = (json $in) var attachments = $json[attachments] if (> (count $attachments) 0) { set @data = (qpdf --list-attachments $in | eawk {|_ f @_| put $f }) } put $data } fn parse-selection {|@selection| var out = [] var in = $nil each {|e| if (is-pdf $e) { if (not-exist $e) { fail (printf '%s: le fichier n’existe pas' $e) } if (not-eq $in $nil) { set @out = $@out $in } set in = [ &file=$e &rotate=$nil &selections=[] ] continue } if (eq $in $nil) { fail (printf '%s: pas de fichier avant la sélection' $e) } var r rc = $in[rotate] $nil if (is-page-selection $e) { set rc = $false } elif (is-rotate-selection $e) { set rc = $true } else { fail (printf '%s: paramètre invalide' $e) } if (not-eq $r $rc) { if (not-eq $r $nil) { set @out = $@out $in set in[selections] = [] } set in[rotate] = $rc } set in[selections] = [ (all $in[selections]) $e ] } $selection if (not-eq $in $nil) { set @out = $@out $in } put $out } fn -t { mktemp -q /tmp/qpdf_XXXXXXXXXX.pdf } fn rotate {|&empty=$false &out=$nil &keep=$false in @rotate| var @args = $in if (eq $out $nil) { set @args = $@args --replace-input } else { set @args = $@args $out } if $empty { set @args = $@args --empty } if $keep { set @rotate = (each {|r| put --rotate=$r } $rotate) qpdf $@args $@rotate return } var tmp = [] try { var @tmp = (each {|r| var t r @s = (-t) (str:split ':' $r) set s = (str:join ':' $s) if (eq $s '') { qpdf $in $t --rotate=$r } else { qpdf $in $t --pages $in $s -- qpdf $t --replace-input --rotate=$r } put $t } $rotate) qpdf $@args --pages $@tmp -- } finally { rm -f $@tmp } } fn -catarg {|selection| var f r s = $selection[file] $selection[rotate] $selection[selections] var out tmp = [] [] if $r { var t = (-t) set @tmp = $t set @out = $@out $t rotate &out=$t $f $@s } else { set @out = $@out $f if (> (count $s) 0) { set @out = $@out (str:join ',' $s) } } put $out $tmp } fn cat {|&empty=$false &collate=$nil &out=$nil @selection| var inputs = (parse-selection $@selection) var pages tmp = [] [] each {|in| var a t = (-catarg $in) set @pages = $@pages $@a set @tmp = $@tmp $@t } $inputs try { var in = $pages[0] var @args = $in if (eq $out $nil) { set @args = $@args --replace-input } else { set @args = $@args $out } if $empty { set @args = $@args --empty } if (bool $collate) { var c = --collate if (is-number $collate) { set c = (str:join '=' [ $c $collate ]) } set @args = $@args $c } qpdf $@args --pages $@pages -- } finally { each {|t| rm -f $t } $tmp } } fn decrypt {|&out=$nil in @passwd| var @args = $in if (eq $out $nil) { set @args = $@args --replace-input } else { set @args = $@args $out } set @args = $@args --decrypt if (> (count $passwd) 0) { set @args = $@args $passwd[0] } qpdf $@args } fn -l0 {|n size| set n = (to-string $n) var l = (- $size (count $n)) str:join '' [ (repeat $l 0) $n ] } fn split {|&empty=$false &size=1 in out| if (not-exist $out) { mkdir $out } var args = [] if $empty { set @args = --empty } qpdf $@args --split-pages=$size $in $out'/%d.pdf' } fn uncat {|&empty=$false in out @selection| if (not-exist $out) { mkdir $out } var i = 1 var n = (count $selection) set n = (count (to-string $n)) each {|s| cat &empty=$empty &out=$out'/'(-l0 $i $n)'.pdf' $in $s set i = (+ $i 1) } $selection } fn raw-stream {|in out id| qpdf $in --show-object=$id --raw-stream-data > $out } fn filtered-stream {|in out id| qpdf $in --show-object=$id --filtered-stream-data --normalize-content=n --stream-data=uncompress --decode-level=all > $out } fn attachment {|in out id| qpdf $in --show-attachment=$id > $out }