elv-lib/mods/pdf.elv

421 lines
8.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use path
use re
use str
use ./common
use ./map
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 is-object {|v|
re:match '^ \d+\ 0\ R$' $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|
common:cexec (has-key $dict $e) { put $dict[$e] } $e
})
} elif (eq $t map) {
map:foreach {|k v|
set o[$k] = (-format $dict $v)
} $o
}
put $o
}
fn -deep {|&dict=$nil objects @keys|
if (== (count $keys) 0) {
common:cexec (not-eq $dict $nil) { -format $dict $objects } $objects
put $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 = (common:cond $extend $objects $nil)
var o _ = (-deep &dict=$dict $objects $id)
put $o
}
fn deep {|&extend=$false in @keys|
var objects = (objects $in)
var dict = (common:cond $extend $objects $nil)
-deep &dict=$dict $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 = [&]
map:foreach {|k o|
if ($cond $o) {
set out[$k] = $o
}
} $objects
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~)
put $fonts | map:foreach {|id f|
map:foreach {|k v|
if (has-key $dict $v) {
set f[$k] = $dict[$v]
}
} $f
var fd = $f[/FontDescriptor]
map:foreach {|k v|
if (and (not-eq $k /FontFile2) (has-key $dict $v)) {
set fd[$k] = $dict[$v]
}
} $fd
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)
map:foreach {|k v|
if (has-key $dict $v) {
set img[$k] = $dict[$v]
}
} $img
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]
map:foreach {|k v|
if (has-key $dict $v) {
set dp[$k] = $dict[$v]
}
} $dp
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 nexiste 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 (common:cond (eq $out $nil) --replace-input $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 (common:cond (eq $out $nil) --replace-input $out)
if $empty {
set @args = $@args --empty
}
if $collate {
var c = --collate(common:cexec (is-number $collate) { put =$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 (common:cond (eq $out $nil) --replace-input $out) --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 = (common:cexec $empty --empty $nop~)
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
}