diff --git a/lib/moi/util/pdf.elv b/lib/moi/util/pdf.elv new file mode 100644 index 0000000..84868d0 --- /dev/null +++ b/lib/moi/util/pdf.elv @@ -0,0 +1,284 @@ +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-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 objects {|in| + var json = (json $in) + put $json[objects] +} + +fn -object {|extend objects key| + if (not (has-key $objects $key)) { + put $nil + return + } + var o = $objects[$key] + if (and $extend (eq (kind-of $o) 'map')) { + keys $o | each {|k| + var v = $o[$k] + if (has-key $objects $v) { + set o[$k] = (-object $extend $objects $v) + } + } + } + put $o +} + +fn object {|&extend=$false in key| + -object $extend (objects $in) $key +} + +fn filter {|&extend=$false in cond| + var objects = (objects $in) + keys $objects | each {|k| + var o = (-object $extend $objects $k) + if ($cond $o) { + put [&id=$k &object=$o] + } + } +} + +fn trailer {|in| + object &extend=$true $in 'trailer' +} + +fn pages {|in| + var json = (json $in) + put $json[pages] +} + +fn nb-pages {|in| + qpdf --show-npages $in +} + +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 +}