edit:add-var pdf~ {|action @args| use flag use math use os use path use re use str use framagit.org/benjamin.vaudour/elv-lib/mods/list use framagit.org/benjamin.vaudour/elv-lib/mods/map use framagit.org/benjamin.vaudour/elv-lib/mods/pdf fn -parse {|rest specs| var flags = [&] var @spec = (each {|s| var d = (map:value-of &default=$false $s 'default') var sh = $s['short'] var lg = $s['long'] if (has-key $s 'default') { assoc $s 'arg-required' $true } else { set d = $false assoc $s 'arg-optional' $true } set flags[$sh] = $d set flags[$lg] = $d } $specs) var parsed argv = (flag:parse-getopt $rest $spec) each {|p| var v = $p['arg'] var s = $p['spec'] var sh = $s['short'] var lg = $s['long'] if (map:value-of &default=$false $s 'arg-optional') { set v = $true } set flags[$sh] = $v set flags[$lg] = $v } $parsed put $flags $argv } fn -size {|s| var b = (num 1024.0) var u = ['' K M G] var i = 0 while (and (< $i 3) (>= $s $b)) { set s i = (/ $s $b) (+ $i 1) } if (> $i 0) { set s = (/ (math:round (* $s 10.0)) 10.0) } str:join '' [ (echo $s) $u[$i] ] } fn -f {|arg| if (eq $arg $nil) { put '' } elif (pdf:is-string $arg) { put $arg } elif (==s (kind-of $arg) 'bool') { if $arg { put 'Oui' } else { put 'Non' } } else { echo $arg } } fn -format {|title header data| var size = [&] each {|c| set size[$c] = (wcswidth $c) } $header each {|line| keys $line | each {|k| var v = $line[$k] var l = (wcswidth $v) if (> $l $size[$k]) { set size[$k] = $l } } } $data echo (styled $title bold yellow) if (== (count $data) 0) { echo 'Aucune donnée…' } else { echo (styled (each {|c| pdf:align $c $size[$c] } $header | str:join ' ') bold underlined) each {|line| echo (each {|c| pdf:align $line[$c] $size[$c] } $header | str:join ' ') } $data } echo } fn help {|@args| e:cat $E:HOME/.config/elvish/aliases/pdf.help } fn compress {|@rest| var flags rest = (-parse $rest [ [&short=r &long=resolution &default=$nil] [&short=t &long=type &default='screen'] ]) var input output = (list:value-of $rest 0) (list:value-of $rest 1) var type resolution = $flags[t] &$flags[r] pdf:must ¬=$true $input {|arg| eq $arg $nil } 'Le fichier d’entrée est obligatoire' pdf:must-pdf-exist $input if (eq $output $nil) { set output = (pdf:output $input '_compressed.pdf') } pdf:must-pdf-not-exist $output pdf:must ['default' 'screen' 'ebook' 'printer' 'prepress'] {|arg| has-value $arg $type} (printf '%s: type invalide' $type) if (not-eq $resolution $nil) { pdf:must $resolution {|arg| and (pdf:is-number $arg) (>= $arg 72) (<= $arg 300) } 'La résolution doit être comprise entre 72 et 300' } pdf:compress &resolution=$resolution &type=$type $input $output } fn decrypt {|input @rest| var output password = (list:value-of &default=(pdf:output $input '_decrypted.pdf') $rest 1) (list:value-of $rest 0) pdf:must-pdf-exist $input pdf:must-pdf-not-exist $output pdf:decrypt &output=$output &password=$password $input } fn gray {|input @rest| var output = (list:value-of &default=(pdf:output $input '_gray.pdf') $rest 0) pdf:must-pdf-exist $input pdf:must-pdf-not-exist $output pdf:gray $input $output } fn info {|input| pdf:must-pdf-exist $input var json = (pdf:json $input) var dict = (pdf:objects $json) var infos = (pdf:object &fmt=$true &rec=$true $dict 'trailer' '/Info') var page = (pdf:page $json 1) var dim = (pdf.deep &dict=$dict $page 'object' '/MediaBox') set dim = [ &w= (pdf:pts2cm $dim[2]) &h= (pdf:pts2cm $dim[3]) ] if (> $dim[w] $dim[h]) { set dim[l] = 'Paysage' } else { set dim[l] = 'Portrait' } var encrypted form = 'Non' 'Non' if (pdf:encryption $json)['encrypted'] { set encrypted = 'Oui' } if (pdf:form $json)['hasacroform'] { set form = 'Oui' } var title = (map:value-of &default='' $infos '/Title') var subject = (map:value-of &default='' $infos '/Subject') var author = (map:value-of &default='' $infos '/Author') var creator = (map:value-of &default='' $infos '/Creator') var producer = (map:value-of &default='' $infos '/Producer') var cdate = (map:value-of &default='' $infos '/CreationDate') var mdate = (map:value-of &default='' $infos '/ModDate') var result = [ &'Chemin'= (path:abs $input) &'Version'= (pdf:Version $json) &'Nombre de pages'= (pdf:nb-pages $input) &'Format des pages'= (printf '%s×%s cm (%s)' $dim[w] $dim[h] $dim[l]) &'Titre'= $title &'Sujet'= $subject &'Auteur'= $author &'Créateur'= $creator &'Producteur'= $producer &'Date de création'= $cdate &'Date de modification'= $mdate &'Chiffré'= $encrypted &'Acroform'= $form ] var l = 1 keys $result | each {|k| var w = (+ (wcswidth $k) 1) if (> $w $l) { set l = $w } } keys $result each {|k| var v = $result[$k] echo (styled (pdf:align $k':' $l)) $v } } fn mono {|@rest| var flags rest = (-parse $rest [ [&short=d &long=density &default=600] ]) var input output = (list:value-of $rest 0) (list:value-of $rest 1) var density = $flags[d] pdf:must ¬=$true $input {|arg| eq $arg $nil } 'Le fichier d’entrée est obligatoire' pdf:must-pdf-exist $input if (eq $output $nil) { set output = (pdf:output $input '_mono.pdf') } pdf:must-pdf-not-exist $output pdf:must $density {|d| and (pdf:is-number $d) (>= $d 72) (<= $d 1200) } 'La densité doit être comprise entre 72 et 1200' pdf:mono &density=$density $input $output } fn merge {|@selection output| pdf:must (count $selection) {|c| > $c 0 } 'La commande doit contenir au moins deux paramètres' pdf:must-pdf-not-exist $output pdf:merge &output=$output $@selection } fn unmerge {|input @selection output| pdf:must {count $selection} {|c| > $c 0} 'La commande doit contenir au moins trois paramètres' pdf:must-pdf-exist $input pdf:must-not-exist $output pdf:unmerge $input $@selection $output } fn split {|@rest input output| var flags _ = (-parse $rest [ [&short=s &long=size &default=1] ]) var size = $flags[s] pdf:must-valid $size {|arg |and (pdf:is-number $arg) (> $arg 0) } pdf:must-pdf-exist $input pdf:must-not-exist $output pdf:split &size=$size $input $output } fn split-at {|rg input output| pdf:must-valid $rg {|arg| re:match '^\d+(,\d+)*$' } var @pages = (str:split ',' $rg | order | compact) var last = 1 var selection = [] each {|p| if (== $p $last) { set @selection = $@selection $p } elif (> $p $last) { set @selection = $@selection (printf '%d-%d' $last $p) } set last = (+ $p 1) } $pages set @selection = $@selection (printf '%d-z' $last) unmerge $input $@selection $output } fn zip {|@rest output| var flags selection = (-parse $rest [ [&short=c &long=collate &default=0] ]) var collate = $flags[c] pdf:must-pdf-not-exist $output pdf:must-valid $collate $pdf:is-number~ pdf:merge &collate &output=$output $@selection } fn unzip {|@rest output| var flags selection = (-parse rest [ [&short=m &long=modulo &default=2] ]) var modulo = $flags[m] pdf:must-valid $modulo {|arg| and (pdf:is-number $arg) (> $arg 2) } pdf:must-not-exist $output var t = (pdf:tmp-pdf) try { pdf:merge &output=$t $@selection var n = (pdf:nb-pages $t) var @sel = (range $modulo | each {|_| put [] }) range $n {|i| var s = (% $i $modulo) set sel[$s] = (conj $sel[$s] (+ $i 1) ) } set @sel = (each {|s| str:join ',' $s } $sel) unmerge $t $@sel $output } catch err { } os:remove-all $t } fn list {|@rest input| pdf:must-pdf-exist $input var flags _ = (-parse $rest [ [&short=a &long=attachments] [&short=f &long=fonts] [&short=i &long=images] ]) var json = (pdf:json $input) if $flags[a] { var data = (pdf:attachments &fmt=$true &rec=$true $json) var @attachments = (keys $data | each {|id| var a = $data[$id] var name = (pdf:deep $a 'filespec' '/F') var size = (pdf:deep $a 'filespec' '/EF' '/F' 'dict' '/Params' '/Size') var mime = (pdf:deep $a 'streams' '/F' 'mimetype') var desc = (pdf:deep $a 'description') put [ &'ID'= $id &'Nom'= $name &'Taille'= (-size $size) &'Mimetype'= (-f $mime) &'Description'= (-f $desc) ] }) -format 'PIÈCES-JOINTES:' ['ID' 'Nom' 'Taille' 'Mimetype' 'Description'] $attachments } if $flags[f] { var data = (pdf:fonts &fmt=$true &rec=$true $json) var @fonts = (each {|f| if (has-key $f '/FontDescriptor') { put $f } } $data | each {|f| var fd = (pdf:deep $f '/FontDescriptor') var id = (pdf:deep $fd '/FontFile2' 'object_id') var file = (pdf:deep $fd '/FontFile2' 'dict') var name = (pdf:deep $fd '/FontName') if (not-eq $name $nil) { set name = (re:replace '^.*\+' '' $name) } var basename type encoding = (put '/BaseFont' '/Subtype' '/Encoding' | each {|k| var v = (pdf:deep $f $k) if (==s (kind-of $v) 'string') { str:trim-left $v '/' } else { put $v } }) put [ &'ID'= $id &'Nom'= (-f $name) &'Nom de base'= (-f $basename) &'Embarqué'= (-f (not-eq $file $nil)) &'Type'= (-f $type) &'Encodage'= (-f $encoding) ] }) -format 'POLICES:' ['ID' 'Nom' 'Nom de base' 'Embarqué' 'Type' 'Encodage'] $fonts } if $flags[i] { var data = (pdf:images &fmt=$true &rec=$true $json) var @images = (each {|i| var dict = (pdf:deep $i 'object' 'dict') var id = (pdf:deep $i 'object' 'object_id') var page = (pdf:deep $i 'page') var w = (pdf:deep $dict '/Width') var h = (pdf:deep $dict '/Height') var size = (pdf:deep $dict '/Length') var bpc = (pdf:deep $dict '/BitsPerComponent') var name = (pdf:deep $i 'name') if (not-eq $name $nil) { set name = (str:trim-left $name '/') } var filter color = (put '/Filter' '/ColorSpace' | each {|k| var v = (pdf:deep $dict $k) if (==s (kind-of $v) 'string') { str:trim-left $v '/' } else { put $v } }) put [ &'ID'= $id &'Page'= (-f $page) &'Nom'= (-f $name) &'Dimensions'= (printf '%d×%d px' $w $h) &'Filtre'= (-f $filter) &'Couleur'= (-f $color) &'BPC'= (-f $bpc) &'Taille'= (-size $size) ] } $data) -format 'IMAGES:' ['ID' 'Page' 'Nom' 'Dimensions' 'Filtre' 'Couleur' 'BPC' 'Taille'] $images } } var actions = [ &help=$help~ &cat=$merge~ &compress=$compress~ &cut=$split-at~ &decrypt=$decrypt~ #&extract=$extract~ &gray=$gray~ &info=$info~ &list=$list~ &merge=$merge~ &mono=$mono~ &split=$split~ &split-at=$split-at~ &uncat=$unmerge~ &unmerge=$unmerge~ &unzip=$unzip~ &zip=$zip~ ] pdf:must $actions {|arg| has-key $arg $action } (printf '%s: action inconnue' $action) $actions[$action] $@args }