use math use path use re use str var cmd = $e:qpdf~ fn -is-pdf {|f| str:has-suffix $f .pdf } fn -is-number {|arg| re:match '^\d+$' $arg } fn -is-rotate {|arg| re:match '^(\+|-)(\d+):.*' $arg } fn -is-seq {|arg| re:match '^\d+(,\d+)*$' $arg } fn -must-pdf {|f| if (not (-is-pdf $f)) { fail $f' n’est pas un fichier PDF' } } fn -must-not-exist {|f| var exist = ?(stat $f 2>&- > /dev/null) if (bool $exist) { fail $f' existe déjà' } } fn -must-exist {|f| var exist = ?(stat $f 2>&- > /dev/null) if (not (bool $exist)) { fail $f' n’existe pas' } } fn -make-tmp { mktemp -q /tmp/qpdf_XXXXXXXXXX.pdf } fn -make-seq {|seq| if (not (-is-seq $seq)) { fail $seq': pas une séquence' } set @seq = (order (str:split , $seq)) var b = 1 each {|ei| var e = (- $ei 1) if (== $b $e) { put $b set b = $ei } elif (< $b $e) { put $b'-'$e set b = $ei } } $seq put $b'-z' } fn -out {|in suffix| var out = (str:trim-suffix $in .pdf) str:join '' [ $out .pdf ] } fn -parse-selection {|in selection| if (-is-rotate $selection) { var out = (-make-tmp) var i = (str:index $selection :) var r sel = $selection[..$i] $selection[(+ $i 1)..] if (eq sel '') { set @sel = $in } else { set @sel = $in $sel } $cmd --empty --pages $@sel -- $out $cmd $out --replace-input --rotate=$r put $out } else { put $in $selection } } fn -parse-all {|args| var tmpfiles = [] var selection = [] var in = $nil var miss = $nil try { each {|e| if (-is-pdf $e) { -must-exist $e if (not-eq $miss $nil) { set @selection = $@selection $miss set miss = $e } set in = $e } else { if (eq $in $nil) { fail 'Le pagerange doit être déclaré après un fichier d’entrée' } var @s = (-parse-selection $in $e) if (== (count $s) 1) { set @tmpfiles = $@tmpfiles $@s } set @selection = $@selection $@s } } $args } finally { put $selection $tmpfiles } } fn -merge {|&collate=$nil selection out| if (< (count $selection) 1) { fail 'Aucune sélection' } var @args = --empty --pages $@selection -- $out if (not-eq $collate $nil) { if (-is-number $collate) { set @args = $collate $@args } set @args = --collate $@args } $cmd $@args } fn -date {|raw| var y m d = $raw[2..6] $raw[6..8] $raw[8..10] var h n s = $raw[10..12] $raw[12..14] $raw[14..16] var tz @_ = (str:split "'" $raw[16..]) var date = (printf ^ '%s-%s-%sT%s:%s:%s%s' ^ $y $m $d ^ $h $n $s ^ $tz) date --date $date } fn -pts2cm {|v| / (math:round (* $v 3.52778)) 100 } fn -size {|size| var u = 0 var m = [ &10=Kio &20=Mio &30=Gio ] while (< $u 30) { var p = (math:pow 2 (+ $u 10)) if (< $size $p) { break } set u = (to-string (+ $u 10)) } if (== $u 0) { put $size } else { var p = (math:pow 2 (+ $u 10)) var e = (/ $size $p) printf '%.1f%s' $e $m[$u] } } fn -b2str {|b| if $b { put 'Oui' } else { put 'Non' } } fn -props {|input json| var objects = $json[objects] var trailer = $objects[trailer] var iid = $trailer[/Info] var rid = $trailer[/Root] var info = $objects[$iid] var root = $objects[$rid] var pages = $objects[$root[/Pages]] var p1 = $objects[$pages[/Kids][0]][/MediaBox] var w = (-pts2cm $p1[2]) var h = (-pts2cm $p1[3]) var layout = 'Portrait' if (> $w $h) { set layout = 'Paysage' } var props = [ &path=(path:abs $input) &fsize=(-size (stat -c '%s' $input)) &version='' &psize=(printf '%sx%s cm (%s)' $w $h $layout) &pages=$pages[/Count] &title='' &subject='' &author='' &producer=$info[/Producer] &creator=$info[/Creator] &cdate=(-date $info[/CreationDate]) &mdate='' &acroform=(-b2str $json[acroform][hasacroform]) &encrypted=(-b2str $json[encrypt][encrypted]) ] #@TODO: version pdf if (has-key $info '/Title') { set props[title] = $info[/Title] } if (has-key $info '/Subject') { set props[subject] = $info[/Subject] } if (has-key $info '/Author') { set props[author] = $info[/Author] } if (has-key $info '/ModDate') { set props[mdate] = (-date $info[/ModDate]) } put $props } fn -list-attachments {|in| $cmd $in --list-attachments | eawk {|_ f @_| put $f } } fn -list-fonts {|in| var objects = ($cmd $in --json | from-json)[objects] keys $objects | each {|k| var o = $objects[$k] if (kind-of $o map) { put $o } } | each {|o| if (has-key $o /Type) { put $o } } | each {|o| if (eq $o[/Type] /FontDescriptor) { var f = [ &name=$o[/FontName][1..] &embedded=(has-key $o /FontFile2) ] if $f[embedded] { set f[file] = $o[/FontFile2] } put $f } } } fn -list-imgs {|in| var objects = ($cmd $in --json | from-json)[objects] keys $objects | each {|k| var o = $objects[$k] if (kind-of $o map) { put $o } } | each {|o| if (has-key $o /Type) { put $o } } | each {|o| if (eq $o[/Type] /FontDescriptor) { var f = [ &name=$o[/FontName][1..] &embedded=(has-key $o /FontFile2) ] if $f[embedded] { set f[file] = $o[/FontFile2] } put $f } } } fn -extract-attachments {|in out| -list-attachments $in | each {|f| $cmd $in --show-attachment=$f > $out'/'$f } } fn -extract-fonts {|in out| -list-fonts $in | each {|f| if $f[embedded] { $cmd $in --show-object=$f[file] --filtered-data-stream > $out'/'$f[name]'.ttf' } } } fn -extract-imgs {|in out| } fn -repeat {|c arg| str:join '' [ (range $c | each {|_| put $arg }) ] } fn -to-string {|v| if (eq (kind-of $v) bool) { if $v { put X } else { put '' } } else { to-string $v } } fn -format-list {|columns data| set @columns = (each {|c| set c[size] = (count $c[label]) put $c } $columns) each {|d| set @columns = (each {|c| var n = $c[name] if (has-key $d $n) { var label = (-to-string $d[$n]) var l = (count $label) if (> $l $c[size]) { set c[size] = $l } } put $c } $columns) } $data var size = 0 var @raw = (each {|c| var cl l = (count $c[label]) $c[size] set size = (+ $size $l 1) str:join '' [ (-repeat (- $l $cl) ' ') $c[label] ] } $columns) echo (str:join ' ' $raw) echo (-repeat $size '-') each {|d| var @raw = (each {|c| var n v = $c[name] '' if (has-key $d $n) { set v = (-to-string $d[$n]) } var l lc = (count $v) $c[size] str:join '' [ (-repeat (- $lc $l) ' ') $v ] } $columns) echo (str:join ' ' $raw) } $data } fn merge {|@args| var l = (- (count $args) 1) if (< $l 1) { fail "la commande doit contenir au moins 2 paramètres" } var out = $args[$l] -must-pdf $out -must-not-exist $out var sel tmp = [] [] try { set sel tmp = (-parse-all $args[..$l]) -merge $sel $out } finally { if (> (count $tmp) 0) { rm -f $@tmp } } } fn split {|@args| var l = (- (count $args) 1) if (< $l 1) { fail "la commande doit contenir au moins 2 paramètres" } var out = $args[$l] -must-pdf $out -must-not-exist $out set args = $args[..$l] var size = $nil if (-is-number $args[0]) { set size @args = @args } var sel tmp = [] [] try { set sel tmp = (-parse-all $args[..$l]) var t = (-make-tmp) -merge $sel $t set @tmp = $@tmp $t var @ac = --split-pages if (not-eq $size $nil) { set @ac = $@ac $size } set @ac = $@ac $t $out/%d.pdf mkdir $out $cmd $@ac } finally { if (> (count $tmp) 0) { rm -f $@tmp } } } fn split-at {|@args| var l = (- (count $args) 1) if (< $l 2) { fail "la commande doit contenir au moins 3 paramètres" } var out = $args[$l] -must-pdf $out -must-not-exist $out var @seq = (-make-seq $args[0]) set args = $args[1..$l] var sel tmp = [] [] try { set sel tmp = (-parse-all $args[..$l]) var t = (-make-tmp) -merge $sel $t set @tmp = $@tmp $t var i = 1 each {|s| $cmd --empty --pages $t $s -- $out/$i.pdf set i = (+ $i 1) } $seq } finally { if (> (count $tmp) 0) { rm -f $@tmp } } } fn zip {|@args| var l = (- (count $args) 1) if (< $l 1) { fail "la commande doit contenir au moins 2 paramètres" } var out = $args[$l] var col = $true -must-pdf $out -must-not-exist $out set args = $args[..$l] if (-is-number $args[0]) { set col @args = $@args } var sel tmp = [] [] try { set sel tmp = (-parse-all $args) -merge &collate=$col $sel $out } finally { if (> (count $tmp) 0) { rm -f $@tmp } } } fn unzip {|@args| var l = (- (count $args) 1) if (< $l 2) { fail 'unzip doit contenir au moins 3 paramètres' } var mod = $args[0] var out = $args[$l] -must-not-exist $out var sel tmp = [] [] try { set sel tmp = (-parse-all $args[1..$l]) var t = (-make-tmp) -merge $sel $t set @tmp = $@tmp $t var @sels = (range $mod | put []) var end = ($cmd --show-npages $t) range $end | each {|i| var m = (% $i $mod) set sel[$m] = (+ $i 1) } mkdir $out range $mod | each {|i| var @pr = (str:join ',' $@sels[$i]) $cmd --empty --pages $t $@pr -- $out'/'(+ $i 1)'.pdf' } } finally { if (> (count $tmp) 0) { rm -f $@tmp } } } fn decrypt {|@args| var l = (count $args) if (or (< $l 1) (> $l 3)) { fail "decrypt doit contenir entre 1 et 3 paramètres" } var in = $args[0] -must-pdf $in -must-exist $in var out = (-out $in _decrypted.pdf) var p = $nil if (> $l 1) { set p = $args[1] if (== $l 3) { set out = $args[2] -must-pdf $out } } -must-not-exist $out if (eq $p $nil) { $cmd $in $out --decrypt } else { $cmd $in $out --decrypt $p } } fn rotate {|@args| var l = (- (count $args) 1) if (< $l 1) { fail "rotate doit contenir au moins 2 paramètres" } var in = $args[0] -must-pdf $in -must-exist $in var out = (-out $in _rotated) if (-is-pdf $args[$l]) { set out = $args[$l] set args = $args[..$l] } -must-not-exist $out var @rotate = (each {|r| put --rotate=$r } $args[1..]) $cmd $in $out $@rotate } fn help {|@args| cat $E:HOME/.config/elvish/aliases/qpdf.help } fn info {|@args| var input = $args[0] var json = ($cmd --json $input | from-json) var props = (-props $input $json) echo ' Chemin: '$props[path] echo ' Taille: '$props[fsize] echo ' Version PDF: '$props[version] echo ' Taille des pages: '$props[psize] echo ' Nombre de pages: '$props[pages] echo '............................................' echo ' Titre: '$props[title] echo ' Sujet: '$props[subject] echo ' Auteur: '$props[author] echo ' Créateur: '$props[creator] echo ' Producteur: '$props[producer] echo ' Date de création: '$props[cdate] echo ' Date de modification: '$props[mdate] echo '............................................' echo ' Chiffré: '$props[encrypted] echo ' Acroform: '$props[acroform] } fn list {|@args| if (!= (count $args) 2) { fail 'list doit comporter 2 paramètres' } var types in = $@args -must-pdf $in -must-exist $in var fl = [ &a=$-list-attachments~ &i=$-list-imgs~ &f=$-list-fonts~ ] var ff = [ &a={|data| each {|d| echo $d } $data } &i={|data| -format-list [ [&name=name &label=Nom] [&name=embedded &label=Inclus] ] $data } &f={|data| -format-list [ [&name=name &label=Nom] [&name=embedded &label=Inclus] ] $data } ] str:split , $types | each {|t| set t = (str:to-lower $t[0]) if (not (has-key $fl $t)) { continue } var @data = ($fl[$t] $in) echo (count $data)' donnée(s) trouvée(s)' echo $ff[$t] $data } } fn extract {|@args| if (!= (count $args) 3) { fail 'extract doit comporter 3 paramètres' } var types in out = $@args -must-pdf $in -must-exist $in -must-not-exist $out var fe = [ &a=$-extract-attachments~ &i=$-extract-imgs~ &f=$-extract-fonts~ ] mkdir $out str:split , $types | each {|t| set t = (str:to-lower $t[0]) if (not (has-key $fe $t)) { continue } $fe[$t] $in $out } } edit:add-var epdf~ {|action @args| var actions = [ &merge=$merge~ &split=$split~ &split-at=$split-at~ &zip=$zip~ &unzip=$unzip~ &decrypt=$decrypt~ &help=$help~ &info=$info~ &rotate=$rotate~ &list=$list~ &extract=$extract~ ] if (not (has-key $actions $action)) { fail $action': action inconnue' } $actions[$action] $@args }