edit:add-var pdf~ {|action @args| use math use os use path use re use str use framagit.org/benjamin.vaudour/elv-lib/mods/common use framagit.org/benjamin.vaudour/elv-lib/mods/format use framagit.org/benjamin.vaudour/elv-lib/mods/map use framagit.org/benjamin.vaudour/elv-lib/mods/pdf var -c~ = $common:cexec~ var -cc~ = {|c f1| -c $c $f1 $nop~} fn must-pdf {|file| -cc (not (pdf:is-pdf $file)) { fail (printf '%s n’est pas un fichier pdf' $file) }} fn must-exist {|file| -cc (pdf:not-exist $file) { fail (printf '%s n’existe pas' $file) }} fn must-not-exist {|file| -cc (pdf:exist $file) { fail (printf '%s existe déjà' $file) }} fn must-pdf-exist {|file| must-pdf $file must-exist $file } fn must-pdf-not-exist {|file| must-pdf $file must-not-exist $file } fn must-valid {|v cond| -cc (not ($cond $v)) { fail (printf '%s: paramètre invalide' $v) }} fn out {|in suffix| var out = (str:trim-suffix $in .pdf) str:join '' [ $out $suffix ] } fn help {|@args| cat $E:HOME/.config/elvish/aliases/pdf.help} fn decrypt {|@args| var l = (count $args) -cc (or (< $l 1) (> $l 3)) { fail 'decrypt doit contenir entre 1 et 3 paramètres' } var in = $args[0] var out = (out $in _decrypted.pdf) var passwd = [] must-pdf-exist $in if (> $l 1) { set @passwd = $args[1] if (> $l 2) { set out = $args[2] } } must-pdf-not-exist $out pdf:decrypt &out=$out $in $@passwd } fn rotate {|@args| var l = (- (count $args) 1) -cc (< $l 1) { fail 'rotate doit contenir au moins 1 paramètres' } var in = $args[0] var out = (out $in _rotated.pdf) if (pdf:is-pdf $args[$l]) { set @args out = (all $args[1..]) } must-pdf-exist $in must-pdf-not-exist $out each {|e| must-valid $e $pdf:is-rotate-selection~ } $args pdf:rotate &keep=$true &out=$out $in $@args } fn merge {|@args| var l = (- (count $args) 1) -cc (< $l 1) { fail 'merge doit contenir au moins 2 paramètres' } var @selection out = $@args must-pdf-not-exist $out var has-in = $false each {|e| if (pdf:is-pdf $e) { must-exist $e set has-in = $true } else { -cc (not $has-in) { fail 'Une sélection doit être précédée d’un fichier d’entrée' } must-valid $e $pdf:is-selection~ } } $selection pdf:cat &out=$out $@selection } fn unmerge {|@args| var l = (- (count $args) 1) -cc (< $l 1) { fail 'unmerge doit contenir au moins 2 paramètres' } var in = $args[0] var out = (out $in _unmerged) if (not (pdf:is-selection $args[$l])) { set @args out = (all $args[1..]) } must-pdf-exist $in must-not-exist $out each {|e| must-valid $e $pdf:is-selection~ } $args pdf:uncat $in $out $@args } fn split {|@args| var l = (- (count $args) 1) -cc (< $l 1) { fail 'split doit contenir au moins 2 paramètres' } var out = $args[$l] set args = $args[..$l] must-not-exist $out var s = 1 if (pdf:is-number $args[0]) { set s @args = $@args } var t = (pdf:-t) try { pdf:cat &out=$t $@args pdf:split &size=$s $t $out } finally { os:remove-all $t } } fn split-at {|@args| var l = (count $args) -cc (!= $l 3) { fail 'split-at doit contenir 3 paramètres' } var rg in out = $@args must-pdf-exist $in must-not-exist $out must-valid $rg {|e| re:match '^\d+(,\d+)*$' $e } var b sel = 1 [] str:split ',' $rg | order | each {|s| var e = (- $s 1) if (== $b $e) { set @sel = $@sel $b set b = $s } elif (< $b $e) { set @sel = $@sel (printf '%d-%d' $b $e) set b = $s } } set @sel = $@sel (printf '%d-z' $b) unmerge $in $@sel $out } fn zip {|@args| var l = (- (count $args) 1) -cc (< $l 1) { fail 'zip doit contenir au moins 2 paramètres' } var @selection out = $@args must-pdf-not-exist $out var has-in = $false each {|e| if (pdf:is-pdf $e) { must-exist $e set has-in = $true } else { -cc (not $has-in) { fail 'Une sélection doit être précédée d’un fichier d’entrée' } must-valid $e $pdf:is-selection~ } } $selection pdf:cat &collate=$true &out=$out $@selection } fn unzip {|@args| var l = (- (count $args) 1) -cc (< $l 1) { fail 'unzip doit contenir au moins 2 paramètres' } var in @sel out = $@args var mod = 2 if (pdf:is-number $in) { set mod = $in -cc (eq (count $sel) 0) { fail 'unzip doit contenir au moins 3 paramètres' } set in @sel = $@sel } must-not-exist $out var t = (pdf:-t) try { pdf:cat &out=$t $in $@sel var n = (pdf:nb-pages $t) os:mkdir $out range 1 (+ 1 $mod) | each {|m| var sel = (range 1 (+ $n 1) | each {|i| var s = (% $i $mod) -cc (== $s 0) { set s = $mod } -cc (== $s $m) $i } | each $to-string~ | str:join ',') pdf:cat &out=(printf '%s/%d.pdf' $out $m) $t $sel } } finally { os:remove-all $t } } fn pts2cm {|v| / (math:round (* $v 3.52778)) 100} 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 rest = [] -cc (> (count $raw[16..]) 0) { set @rest = (str:split "'" $raw[16..]) } var tz = '+00:00' if (> (count $rest) 0) { var tz = $rest[0] -cc (eq $tz[0] 'Z') { set tz = $tz[1..] } -cc (eq $tz '') { set tz = '00' } -cc (and (not-eq $tz[0] '+') (not-eq $tz[0] '-')) { set tz = (printf '+%s' $tz) } var mm = '00' -cc (and (> (count $rest) 1) (pdf:is-number $rest[1])) { set mm = $rest[1] } set tz = (printf '%s:%s' $tz $mm) } var date = (printf ^ '%s-%s-%sT%s:%s:%sZ%s' ^ $y $m $d ^ $h $n $s ^ $tz) date --date $date } fn info {|@args| -cc (!= (count $args) 1) { fail 'info doit contenir un paramètre' } var in @_ = $@args must-pdf-exist $in var json = (pdf:json $in) var objects = $json[objects] var trailer = $objects[trailer] var infos = $objects[$trailer[/Info]] var pages = $json[pages] var p1 = $objects[$pages[0][object]] var w h = (all $p1[/MediaBox][2..] | each $pts2cm~) var layout = (-c (> $w $h) 'Paysage' 'Portrait') var title = (map:value-of $infos '/Title') var subject = (map:value-of $infos '/Subject') var author = (map:value-of $infos '/Author') var creator = (map:value-of $infos '/Creator') var producer = (map:value-of $infos '/Producer') var mdate = (-c (has-key $infos '/ModDate') { -date $infos[/ModDate] } '') var crypted = (-c (pdf:encryption $in)[encrypted] 'Oui' 'Non') var hasform = ( -c (pdf:form $in)[hasacroform] 'Oui' 'Non') var data = [ [&k='Chemin' &v=(path:abs $in)] [&k='Taille' &v=(format:size (stat -c '%s' $in))] [&k='Version PDF' &v=(pdf:version $in)] [&k='Nombre de pages' &v=(count $pages)] [&k='Format des pages' &v=(printf '%sx%s cm (%s)' $w $h $layout)] [&k='Titre' &v=$title] [&k='Subjet' &v=$subject] [&k='Auteur' &v=$author] [&k='Créateur' &v=$creator] [&k='producteur' &v=$producer] [&k='Date de création' &v=(-date $infos[/CreationDate])] [&k='Date de modification' &v=$mdate] [&k='Chiffré' &v=$crypted] [&k='Acroform' &v=$hasform] ] var label = (format:repeat 21 ' ') var @props = (format:column &align=right k $label) (format:column v $label) set props = (format:update-props $props $data) var sep = (format:repeat (+ $props[0][size] $props[1][size] 3) '.') var @part = $data[..5] $data[5..12] $data[12..] each {|d| format:list &with-header=$false &csep=' : ' &recompute=$false $props $@d echo $sep } $part } fn list-attachments {|in| all (pdf:attachments $in)} fn display-attachments {|in| var @data = (list-attachments $in) printf "%d pièce(s)-jointe(s) trouvée(s):\n\n" (count $data) each $echo~ $data echo } fn extract-attachments {|in out| list-attachments $in | each {|f| pdf:attachment $in $out/$f $f }} fn list-fonts {|in| pdf:fonts $in | each {|f| -cc (has-key $f /FontDescriptor) { put [&font=$f &fd=$f[/FontDescriptor]] } } | each {|f| -cc (has-key $f[fd] /FontName) { assoc $f name $f[fd][/FontName] } } | each {|f| var @_ n = (str:split '+' $f[name]) var font = $f[font] var fd = $f[fd] var embed = (has-key $fd /FontFile2) var file = (-c $embed $fd[/FontFile2] '') set n = (str:trim-prefix $n '/') put [ &id=$font[id] &name=$n &embed=$embed &file=$file &type=(str:trim-prefix $font[/Subtype] '/') &encoding=(str:trim-prefix $font[/Encoding] '/') ] } } fn display-fonts {|in| var @data = (list-fonts $in) printf "%d police(s) trouvée(s):\n\n" (count $data) var props = [ (format:column &align=center id ID) (format:column name Nom) (format:column type Type) (format:column encoding Encodage) (format:column &align=center embed Téléchargeable) (format:column &align=center file Fichier) ] format:list $props $@data echo } fn extract-fonts {|in out| list-fonts $in | each {|f| -cc $f[embed] { pdf:filtered-stream $in (printf '%s/%s.ttf' $out $f[name]) $f[file] } } } fn list-images {|in| pdf:images $in | each {|i| var w = (format:int $i[/Width]) var h = (format:int $i[/Height]) var c = (-c (has-key $i /ColorSpace) { put $i[/ColorSpace][0] } '') put [ &id=$i[id] &page=(format:int $i[page]) &name=(str:trim-prefix $i[name] '/') &width=$w &height=$h &dim=(printf '%dx%d' $w $h) &filter=(str:trim-prefix $i[/Filter] '/') &size=(format:size $i[/Length]) &bpc=(format:int $i[/BitsPerComponent]) &color=(str:trim-prefix $c '/') ] } var objects = (pdf:objects $in) all (pdf:pages $in) | each {|p| var pn = (format:int $p[pageposfrom1]) all $p[images] | each {|img| var id = $img[object] var o = (pdf:-object &extend=$true $objects $id) var s = $o[/Length] var c = (-c (has-key $o /ColorSpace) { put $o[/ColorSpace][0] } '') var w = (format:int $o[/Width]) var h = (format:int $o[/Height]) put [ &id=$id &page=$pn &name=(str:trim-prefix $img[name] '/') &width=$w &height=$h &dim=(printf '%dx%d' $w $h) &filter=(str:trim-prefix $o[/Filter] '/') &size=(format:size $s) &bpc=(format:int $o[/BitsPerComponent]) &color=(str:trim-prefix $c '/') ] } } } fn display-images {|in| var @data = (list-images $in) printf "%d image(s) trouvée(s):\n\n" (count $data) var props = [ (format:column &align=right page Page) (format:column &align=center id ID) (format:column name Nom) (format:column &align=right size Taille) (format:column &align=right dim Dimensions) (format:column color Couleur) (format:column &align=right bpc BPC) (format:column &align=center filter Filtre) ] format:list $props $@data echo } fn extract-images {|in out| # En attendant de savoir décoder correctement les images, on utilise poppler pdfimages -all $in $out/Im } fn list {|@args| -cc (!= (count $args) 2) { fail 'list doit comporter 2 paramètres' } var types in = $@args must-pdf-exist $in var display = [ &a=$display-attachments~ &f=$display-fonts~ &i=$display-images~ ] str:split ',' $types | each {|t| set t = (str:to-lower $t) must-valid $t {|e| and (> (count $e) 0) (has-key $display $e[0])} $display[$t[0]] $in } } fn extract {|@args| -cc (!= (count $args) 3) { fail 'extract doit comporter 3 paramètres' } var types in out = $@args must-pdf-exist $in must-not-exist $out var extr = [ &a=$extract-attachments~ &f=$extract-fonts~ &i=$extract-images~ ] try { os:mkdir $out str:split ',' $types | each {|t| set t = (str:to-lower $t) must-valid $t {|e| and (> (count $e) 0) (has-key $extr $e[0])} $extr[$t[0]] $in $out } } catch e { os:remove-all $out fail $e } } fn gray {|@args| -cc (!= (count $args) 2) { fail 'gray doit comporter 2 paramètres' } var in out = $@args must-pdf-exist $in must-pdf-not-exist $out convert -colorspace gray $in $out } fn mono {|@args| var c = (count $args) -cc (or (< $c 2) (> $c 3)) { fail 'mono doit comporter entre 2 et 3 paramètres' } var t = $nil if (pdf:is-number $args[0]) { set t @args = $@args -cc (or (< $t 0) (> $t 100)) { fail le taux doit être compris entre 0 et 100 } -cc (< $c 2) { fail 'mono doit comporter 3 paramètres' } } var in out = $@args must-pdf-exist $in must-pdf-not-exist $out if $t { convert -colorspace gray -threshold $t'%' $in $out } else { convert -monochrome $in $out } } var actions = [ &help=$help~ &decrypt=$decrypt~ &rotate=$rotate~ &merge=$merge~ &cat=$merge~ &unmerge=$unmerge~ &uncat=$unmerge~ &split=$split~ &split-at=$split-at~ &cut=$split-at~ &zip=$zip~ &unzip=$unzip~ &info=$info~ &list=$list~ &extract=$extract~ &gray=$gray~ &mono=$mono~ #&compress=$compress~ ] -cc (not (has-key $actions $action)) { fail (printf '%s: action inconnue' $action) } $actions[$action] $@args }