Contents
pdf2book
Make any pdf in format A4 a book in format A5 and bind it e.g. with rings.
Rearrange the pages
How it works (schematically):
PDF -> PS
- Rotate specific pages
- Scramble pages (like in a book)
- Pack two pages on one page
PS -> PDF
Comments
- The script works in the CWD and takes a path to the original PDF-file, which is only read.
- Make sure to match the correct input size or the output will not be centered well.
- The input paper size is by default determined automatically, but may also be specified manually or as a preset.
When probing manually you may use the psnup option -d
As an example page "45" is rotated via VARIABLE LAYOUT
- Scaled down by a factor to allow for some margin to cut and bind the page.
This little script has no additional, but in the end gets the job done
/usr/local/bin/pdf2book.sh
1 #!/bin/bash
2
3 FILE_PATH="$1"
4 FILE="$(basename "$FILE_PATH")"
5 TITLE="${FILE%%\.pdf}"
6 #LAYOUT="A1-44 A45south A46-end"
7 LAYOUT="A1-end"
8
9 pdftk A="$FILE_PATH" \
10 cat $LAYOUT \
11 output "$TITLE"_rot.pdf
12 pdftops "$TITLE"_rot.pdf "$TITLE".ps
13 psbook -q "$TITLE".ps "$TITLE"_BOOK.ps
14
15 PAGES_PER_PAGE=2
16 ### OVERRIDE /etc/papersize ON DEBIAN
17 PAPERSIZE_OUTPUT=a4
18 ### MAKE SURE TO MATCH THE CORRECT INPUT PAPERSIZE
19 ### YOU MAY CHECK THIS WITH OPTION "-d"
20
21 PAPERSIZE_INPUT=auto
22 case "$PAPERSIZE_INPUT" in
23 "a0"|"a1"|"a2"|"a3"|"a4"|"a5"|"b5"|"letter"|"legal"|"tabloid"|"statement"|"executive"|"folio"|"quarto"|"10x14")
24 PAPERSIZE_INPUT_OPTIONS="-P $PAPERSIZE_INPUT"
25 ;;
26 "auto")
27 read -r PAPERSIZE_INPUT_WIDTH PAPERSIZE_INPUT_HEIGHT <<< \
28 "$(identify -verbose "$FILE_PATH" \
29 |grep "Print size" \
30 |head -n1 \
31 |grep -Eo '([-+]?[0-9]*\.?[0-9]+)+'\
32 |tr '\n' ' ')"
33 PAPERSIZE_INPUT_WIDTH="$(units -t "$PAPERSIZE_INPUT_WIDTH inch" 'mm')mm"
34 PAPERSIZE_INPUT_HEIGHT="$(units -t "$PAPERSIZE_INPUT_HEIGHT inch" 'mm')mm"
35 PAPERSIZE_INPUT_OPTIONS="-H $PAPERSIZE_INPUT_HEIGHT -W $PAPERSIZE_INPUT_WIDTH"
36 ;;
37 "manual")
38 PAPERSIZE_INPUT_WIDTH="207mm"
39 PAPERSIZE_INPUT_HEIGHT="285mm"
40 PAPERSIZE_INPUT_OPTIONS="-H $PAPERSIZE_INPUT_HEIGHT -W $PAPERSIZE_INPUT_WIDTH"
41 ;;
42 *)
43 echo "Error. Exiting …"
44 exit 1
45 ;;
46 esac
47
48 ### ADJUST OUTPUT USING
49 ### SCALE AND MARGINS
50 SCALE=0.69
51 MARGIN_PAGE_WHOLE=1.5mm
52 MARGIN_PAGE=2mm
53
54 psnup -q -l \
55 -"$PAGES_PER_PAGE" \
56 -p "$PAPERSIZE_OUTPUT" \
57 $PAPERSIZE_INPUT_OPTIONS \
58 -s "$SCALE" \
59 -b "$MARGIN_PAGE_WHOLE" \
60 -m "$MARGIN_PAGE" \
61 "$TITLE"_BOOK.ps \
62 "$TITLE"_SIG.ps
63 ps2pdf "$TITLE"_SIG.ps
64 rm "$TITLE"{,_BOOK,_SIG}.ps
Dependencies
Printing
duplex short edge on A4
- auto center
- no scaling
- no border control
- Cut the page in the middle of the margin strip
- bind
Reduce Size of PDF - v1
High-quality scans can be huge, since the images are simply embedded. This a little script that reduces pdfs in size effectively using ghostscript.
1 aptitude install ghostscript
Create a script which is in a path within your environment varibale PATH e.g. /usr/local/bin/pdf_minify.sh
1 #!/bin/bash
2
3 ### INIT
4 SRC="$1"
5 DST="$2"
6
7 ### SANITIZE
8 if [ -z "$SRC" ]; then
9 echo "Please specify a source file."
10 exit 2
11 fi
12
13 if [ -z "$DST" ]; then
14 echo "Please specify a destination file."
15 exit 2
16 fi
17
18 if [ "$SRC" = "$DST" ]; then
19 echo "Both files must not match."
20 exit 2
21 fi
22
23 set -eu
24 ps2pdf -dDetectDuplicateImages=true \
25 -dCompressFonts=true \
26 -dEmbedAllFonts=false \
27 -dSubsetFonts=true \
28 -dPDFSETTINGS=/ebook \
29 "$SRC" "$DST"
Make it executable
1 chmod a+x /usr/local/bin/pdf_minify.sh
Hint: Change to destination directory and wrap it in a for-loop:
Comparison:
1 % ll -Rh
2 .:
3 insgesamt 7.3M
4 -rw-r--r-- 1 user user 842K Jul 4 14:25 G1.pdf
5 -rw-r--r-- 1 user user 4.3M Jun 17 11:13 G2.pdf
6 -rw-r--r-- 1 user user 976K Jul 4 14:24 K1.pdf
7 drwxr-xr-x 1 user user 326 Jul 4 14:47 minimal
8 -rw-r--r-- 1 user user 1.3M Jul 4 14:32 R1.pdf
9
10 ./minimal:
11 insgesamt 1.8M
12 -rw-r--r-- 1 user user 69K Jul 4 14:47 G1.pdf
13 -rw-r--r-- 1 user user 1.2M Jul 4 14:47 G2.pdf
14 -rw-r--r-- 1 user user 270K Jul 4 14:47 K1.pdf
15 -rw-r--r-- 1 user user 343K Jul 4 14:47 R1.pdf
Reduce Size of PDF - v2
Here is a more comfortable version of the script. Which can:
- replace the files and create backups,
- choose the name itself based on a suffix,
- output to another directory
- process multiple files.
When copying the script, remember that HereDocs with <<- only strip tabs ^I. So in front of EOH only tabs may be found.
/usr/local/bin/pdf_minify.sh
1 #!/bin/bash
2
3 ### DEFAULTS
4 RUN_ONCE=false
5 INPLACE=false
6 BACKUP=false
7 SUFFIX='minified'
8 SUFFIX_APPEND=true
9 SUFFIX_APPEND_FORCE=false
10
11 SELF="$(basename "$0")"
12
13 usage () {
14 cat <<-EOH
15
16 $SELF [options] [--] file1 [file2, …]
17
18 Options:
19 -b|--backup [yn] Force override creation of backup file
20 in the same directory as the input file.
21 -d|--dir-out Create directory and
22 write output in this directory.
23 No backup file is written.
24 -h|--help Display this help.
25 -i|--in-place Replace input file.
26 Creates a backup.
27 -o|--out Path to the output file and
28 process only one pdf.
29 -s|--suffix [opt] Suffix is appended after a underscore "_".
30 Maybe specified up to 2 times because
31 it has an optional argument
32 (with a bit special parsing).
33 Without opt Force append suffix.
34 With opt Change suffix (default: minified).
35 Shortform Don't separate with a space.
36 (examples:
37 correct: '-sCUSTOMSUFFIX'
38 false: '-s CUSTOMSUFFIX')
39 Longform Separate with a "="
40 (examples:
41 correct: '--suffix=CUSTOMSUFFIX'
42 false: '--suffix CUSTOMSUFFIX')
43 EOH
44 }
45
46 # Note that we use "$@" to let each command-line parameter expand to a
47 # separate word. The quotes around "$@" are essential!
48 # We need TEMP as the 'eval set --' would nuke the return value of getopt.
49 TEMP=$(getopt \
50 -o 'b:d:hio:s::' \
51 --long 'backup:,dir-out:,help,in-place,out:,suffix::' \
52 -n "$SELF" -- "$@")
53
54 if [ $? -ne 0 ]; then
55 echo 'Terminating...' >&2
56 exit 1
57 fi
58
59 # Note the quotes around "$TEMP": they are essential!
60 eval set -- "$TEMP"
61 unset TEMP
62
63 while true; do
64 case "$1" in
65 '-d'|'--dir-out')
66 DIR_OUT="$2"
67 SUFFIX_APPEND=false
68 BACKUP=false
69 shift 2
70 continue
71 ;;
72 '-h'|'--help')
73 usage
74 exit 0
75 ;;
76 '-i'|'--in-place')
77 INPLACE=true
78 SUFFIX_APPEND=false
79 BACKUP=true
80 shift
81 continue
82 ;;
83 '-b'|'--backup')
84 BACKUP_FORCE="$2"
85 shift 2
86 continue
87 ;;
88 '-o'|'--out')
89 OUT="$2"
90 shift 2
91 continue
92 ;;
93 '-s'|'--suffix')
94 # optional argument. As we are in quoted mode,
95 # an empty parameter will be generated if its optional
96 # argument is not found.
97 case "$2" in
98 '')
99 SUFFIX_APPEND_FORCE=true
100 ;;
101 *)
102 SUFFIX="$2"
103 ;;
104 esac
105 shift 2
106 continue
107 ;;
108 '--')
109 shift
110 break
111 ;;
112 *)
113 echo 'Internal error!' >&2
114 exit 1
115 ;;
116 esac
117 done
118
119 ### SANITIZE
120 if [ "${#@}" -lt 1 ]; then
121 echo "Please specify at least one source file."
122 exit 2
123 fi
124
125 if [ -n "$OUT" ]; then
126 cat <<-EOH
127 Output file has been given.
128 Processing only first argument.
129 EOH
130 fi
131
132 ### MAIN
133
134 ### FORCE OVERRIDE BACKUP DEFAULTS
135 if [ -n "$BACKUP_FORCE" ]; then
136 if grep -Ei '^(y(es)?|true)$' <<< "$BACKUP_FORCE";then
137 BACKUP=true
138 elif grep -Ei '^(no?|false)$' <<< "$BACKUP_FORCE";then
139 BACKUP=false
140 else
141 echo "Unknown switch '$BACKUP_FORCE'"
142 fi
143 fi
144
145 ### CREATE DESTINATION DIRECTORY
146 [ -n "$DIR_OUT" ] && [ ! -d "$DIR_OUT" ] \
147 && mkdir "$DIR_OUT"
148
149 ### CREATE A TEMPORARY DIRECTORY
150 DIR_TMP="$(mktemp -d)"
151
152 for ARG; do
153 echo "Processing '$ARG'"
154
155 ### CREATE A BACKUP AND LEAVE IT UNTOUCHED
156 if $BACKUP \
157 && ! cp -p "$ARG" "${DIR_OUT}${ARG%.pdf}_backup.pdf"; then
158 cat <<-EOH
159 Backup of file "$SRC" to "${DIR_OUT}${SRC%.pdf}_backup.pdf" failed.
160 Exiting…
161 EOH
162 exit 1
163 fi
164
165 ### CREATE A TEMPORARY WORKING COPY
166 cp "$ARG" "$DIR_TMP"
167 BASENAME="$(basename "$ARG")"
168
169 IN="$DIR_TMP/$BASENAME"
170
171 if [ -n "$OUT" ];then
172 RUN_ONCE=true
173 else
174 OUT="$ARG"
175 fi
176
177 if ! $INPLACE && [ -n "$DIR_OUT" ]; then
178 OUT="$DIR_OUT/$BASENAME"
179 fi
180
181 $SUFFIX_APPEND || $SUFFIX_APPEND_FORCE \
182 && OUT="${OUT%.pdf}_${SUFFIX}.pdf"
183
184 ps2pdf -dDetectDuplicateImages=true \
185 -dCompressFonts=true \
186 -dEmbedAllFonts=false \
187 -dSubsetFonts=true \
188 -dPDFSETTINGS=/ebook \
189 "$IN" "$OUT"
190
191 $RUN_ONCE && break
192 unset OUT
193 done
194
195 ### CLEANUP
196 rm -r "$DIR_TMP"
Just invoke it like this
PDF Web-Optimization - Linearization
It's not all about size. With larger PDFs it is possible to start displaying content while still downloading the file. The PDF just has to be linearized, e.g. with qpdf
1 qpdf --linearize --replace-input input.pdf
2 ### RECURSIVELY FIND AND OPTIMIZE DOCUMENTS
3 find . -name '*.pdf' \
4 | xargs -P 16 -L1 -I{} \
5 qpdf --linearize --replace-input "{}"
6 ###YOU MAY HAVE TO CHECK AND DELETE ".~qpdf-orig" FILES
7 ###WHICH ARE CREATED BY QPDF ON WARNING OR ERROR
8 find . -name '*.~qpdf-orig' #-delete
9
Join PDFs into one
Make sure you got the correct sequence of PDFs.
With ghostscript
Picture to PDF
The µS0ft ticket system does filter normal PNGs, but accepts PDF …
So we just convert the PNGs to PDFs with ImageMagick
You may get an error like the following
convert-im6.q16: attempt to perform an operation not allowed by the security policy `PDF' @ error/constitute.c/IsCoderAuthorized/421.
There is now a security policy that disabled ghost-script formats
/etc/ImageMagick-6/policy.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE policymap [
3 <!ELEMENT policymap (policy)*>
4 <!ATTLIST policymap xmlns CDATA #FIXED ''>
5 <!ELEMENT policy EMPTY>
6 <!ATTLIST policy xmlns CDATA #FIXED '' domain NMTOKEN #REQUIRED
7 name NMTOKEN #IMPLIED pattern CDATA #IMPLIED rights NMTOKEN #IMPLIED
8 stealth NMTOKEN #IMPLIED value CDATA #IMPLIED>
9 ]>
10 <!--
11 Configure ImageMagick policies.
12
13 Domains include system, delegate, coder, filter, path, or resource.
14
15 Rights include none, read, write, execute and all. Use | to combine them,
16 for example: "read | write" to permit read from, or write to, a path.
17
18 Use a glob expression as a pattern.
19
20 Suppose we do not want users to process MPEG video images:
21
22 <policy domain="delegate" rights="none" pattern="mpeg:decode" />
23
24 Here we do not want users reading images from HTTP:
25
26 <policy domain="coder" rights="none" pattern="HTTP" />
27
28 The /repository file system is restricted to read only. We use a glob
29 expression to match all paths that start with /repository:
30
31 <policy domain="path" rights="read" pattern="/repository/*" />
32
33 Lets prevent users from executing any image filters:
34
35 <policy domain="filter" rights="none" pattern="*" />
36
37 Any large image is cached to disk rather than memory:
38
39 <policy domain="resource" name="area" value="1GP"/>
40
41 Use the default system font unless overwridden by the application:
42
43 <policy domain="system" name="font" value="/usr/share/fonts/favorite.ttf"/>
44
45 Define arguments for the memory, map, area, width, height and disk resources
46 with SI prefixes (.e.g 100MB). In addition, resource policies are maximums
47 for each instance of ImageMagick (e.g. policy memory limit 1GB, -limit 2GB
48 exceeds policy maximum so memory limit is 1GB).
49
50 Rules are processed in order. Here we want to restrict ImageMagick to only
51 read or write a small subset of proven web-safe image types:
52
53 <policy domain="delegate" rights="none" pattern="*" />
54 <policy domain="filter" rights="none" pattern="*" />
55 <policy domain="coder" rights="none" pattern="*" />
56 <policy domain="coder" rights="read|write" pattern="{GIF,JPEG,PNG,WEBP}" />
57 -->
58 <policymap>
59 <!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
60 <policy domain="resource" name="memory" value="256MiB"/>
61 <policy domain="resource" name="map" value="512MiB"/>
62 <policy domain="resource" name="width" value="16KP"/>
63 <policy domain="resource" name="height" value="16KP"/>
64 <!-- <policy domain="resource" name="list-length" value="128"/> -->
65 <policy domain="resource" name="area" value="128MP"/>
66 <policy domain="resource" name="disk" value="1GiB"/>
67 <!-- <policy domain="resource" name="file" value="768"/> -->
68 <!-- <policy domain="resource" name="thread" value="4"/> -->
69 <!-- <policy domain="resource" name="throttle" value="0"/> -->
70 <!-- <policy domain="resource" name="time" value="3600"/> -->
71 <!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
72 <!-- <policy domain="module" rights="none" pattern="{PS,PDF,XPS}" /> -->
73 <!-- <policy domain="path" rights="none" pattern="@*" /> -->
74 <!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
75 <!-- <policy domain="cache" name="synchronize" value="True"/> -->
76 <!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/>
77 <!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
78 <!-- <policy domain="system" name="shred" value="2"/> -->
79 <!-- <policy domain="system" name="precision" value="6"/> -->
80 <!-- <policy domain="system" name="font" value="/path/to/font.ttf"/> -->
81 <!-- <policy domain="system" name="pixel-cache-memory" value="anonymous"/> -->
82 <!-- <policy domain="system" name="shred" value="2"/> -->
83 <!-- <policy domain="system" name="precision" value="6"/> -->
84 <!-- not needed due to the need to use explicitly by mvg: -->
85 <!-- <policy domain="delegate" rights="none" pattern="MVG" /> -->
86 <!-- use curl -->
87 <policy domain="delegate" rights="none" pattern="URL" />
88 <policy domain="delegate" rights="none" pattern="HTTPS" />
89 <policy domain="delegate" rights="none" pattern="HTTP" />
90 <!-- in order to avoid to get image with password text -->
91 <policy domain="path" rights="none" pattern="@*"/>
92 <!-- disable ghostscript format types -->
93 <policy domain="coder" rights="none" pattern="PS" />
94 <policy domain="coder" rights="none" pattern="PS2" />
95 <policy domain="coder" rights="none" pattern="PS3" />
96 <policy domain="coder" rights="none" pattern="EPS" />
97 <policy domain="coder" rights="none" pattern="PDF" />
98 <policy domain="coder" rights="none" pattern="XPS" />
99 </policymap>
Change this
1 <policy domain="coder" rights="none" pattern="PDF" />
into
1 <policy domain="coder" rights="read|write" pattern="PDF" />
And the conversion works like a charm.
Delete page from a PDF
If you have a scan with many blank pages, you may want to delete some pages.
Take a look at pdftk for this purpose.
1 pdftk in.pdf cat 1-12 14-end output out1.pdf
Plasma Dolphin PDF Preview
Install a plugin
1 apt install kdegraphics-thumbnailers
Configure Dolphin to preview PDFs.
pdf_split_pages.sh
Select pages from PDF files and save them as single or multiple standalone PDFs.
/usr/local/bin/pdf_split_pages.sh
1 #!/bin/bash
2
3 DELIM_LIST=','
4 DELIM_INCR='_'
5 DELIM_RANGE='-'
6
7 SINGLE=false
8 PROGRAM="${0#*/}"
9
10 if [ "$PROGRAM" = "range_list.sh" ]; then
11 RANGE_INCR="${2:-1}"
12 RANGE_LIST_INPUT="$1"
13 else
14 RANGE_INCR="1"
15 fi
16
17 usage () {
18 cat <<-EOF
19
20 Synopsis:
21 $PROGRAM (options) [--] file1.pdf [file2.pdf [fileN.pdf]]
22
23 Parameters:
24 [-h|--help] Print this page
25 (-p|--pages) Select pages as range list (-p '1,5-6,23')
26 [-s|--single] Assemble selected pages to single output per input file
27 [--] End parsing of switches
28
29 () Mandatory
30 [] Optional
31 EOF
32 }
33
34 # Note that we use "$@" to let each command-line parameter expand to a
35 # separate word. The quotes around "$@" are essential!
36 # We need TEMP as the 'eval set --' would nuke the return value of getopt.
37 TEMP=$(getopt -o 'hp:s' --long 'help,pages:i,single' -n "$PROGRAM" -- "$@")
38
39 if [ $? -ne 0 ]; then
40 echo 'Terminating...' >&2
41 exit 1
42 fi
43
44 # Note the quotes around "$TEMP": they are essential!
45 eval set -- "$TEMP"
46 unset TEMP
47
48 while true; do
49 case "$1" in
50 '-h'|'--help')
51 usage
52 exit
53 ;;
54 '-p'|'--pages')
55 RANGE_LIST_INPUT="$2"
56 shift 2
57 continue
58 ;;
59 '-s'|'--single')
60 SINGLE=true
61 shift
62 continue
63 ;;
64 #'-c'|'--c-long')
65 # # c has an optional argument. As we are in quoted mode,
66 # # an empty parameter will be generated if its optional
67 # # argument is not found.
68 # case "$2" in
69 # '')
70 # echo 'Option c, no argument'
71 # ;;
72 # *)
73 # echo "Option c, argument '$2'"
74 # ;;
75 # esac
76 # shift 2
77 # continue
78 #;;
79 '--')
80 shift
81 break
82 ;;
83 *)
84 echo 'Internal error!' >&2
85 exit 1
86 ;;
87 esac
88 done
89
90 if [ -z "$RANGE_LIST_INPUT" ]; then
91 echo "Please specify a page range."
92 exit 2
93 fi
94
95 if [ ${#@} == 0 ]; then
96 echo "Please specify the input files."
97 usage
98 exit 2
99 else
100 IN_FILES=( "$@" )
101 echo "Input files: '${IN_FILES[*]}'"
102 fi
103
104 seq_bc() (
105 local MIN="$1"
106 local INCR="$2"
107 local MAX="$3"
108 echo "for (i = $MIN; i <= $MAX; i+=$INCR) i" | bc -l
109 )
110
111 split_list () {
112 local LIST="$1"
113 local DELIM_LIST="${2:-$DELIM_LIST}"
114 local -a RETURN
115
116 readarray -td "$DELIM_LIST" RETURN < <(echo -n "$LIST")
117
118 printf '%s\n' "${RETURN[@]}"
119 }
120
121 expand_range () {
122 local RANGE_INPUT="$1"
123 local DELIM_INCR="${2:-$DELIM_INCR}"
124 local DELIM_RANGE="${3:-$DELIM_RANGE}"
125 local INCR="$RANGE_INCR"
126 local MIN MAX
127 local -a RETURN
128
129 RANGE="$(cut -f1 -d"$DELIM_INCR" <<< "$RANGE_INPUT")"
130 if grep -q "$DELIM_INCR" <<< "$RANGE_INPUT"; then
131 INCR="$(cut -f2 -d"$DELIM_INCR" <<< "$RANGE_INPUT")"
132 fi
133
134 MIN="$(cut -f1 -d"$DELIM_RANGE" <<< "$RANGE")"
135 if grep -q "$DELIM_RANGE" <<< "$RANGE"; then
136 MAX="$(cut -f2 -d"$DELIM_RANGE" <<< "$RANGE")"
137 else
138 MAX="$MIN"
139 fi
140
141 if [ "$MIN" = "$MAX" ]; then
142 RETURN=( "$MIN" )
143 else
144 readarray -t RETURN < <(seq_bc "$MIN" "$INCR" "$MAX")
145 fi
146
147 printf '%s\n' "${RETURN[@]}"
148 }
149
150 expand_range_list () {
151 local RANGE_LIST="$1"
152 local RANGE
153 local -a RETURN
154 local -a RANGES
155 local -a RANGE_EXPANDED
156
157 readarray -t RANGES < <(split_list "$RANGE_LIST")
158
159 for RANGE in "${RANGES[@]}"; do
160 readarray -t RANGE_EXPANDED < <(expand_range "$RANGE")
161 RETURN+=( "${RANGE_EXPANDED[@]}" )
162 unset RANGE_EXPANDED
163 done
164
165 readarray -t RETURN \
166 < <(printf "%s\n" "${RETURN[@]}" |sort -nu)
167
168 printf '%s\n' "${RETURN[@]}"
169 }
170
171 readarray -t RANGE_LIST_EXPANDED \
172 < <(expand_range_list "$RANGE_LIST_INPUT")
173
174 echo "Pages: ${RANGE_LIST_EXPANDED[*]}"
175
176 ### QPDF DOCS
177 ### https://qpdf.readthedocs.io/en/stable/cli.html
178
179 ### EXTRACT PAGES
180 for FILE_PDF in "$@"; do
181 if [ ! -f "$FILE_PDF" ]; then
182 echo "File '$FILE_PDF' does not exist. Skipping …"
183 continue
184 fi
185
186 FILE="${FILE_PDF/%.pdf/}"
187 echo "FILE: $FILE"
188 if $SINGLE; then
189 OUT="${FILE}_single.pdf"
190 qpdf --empty --pages "$FILE_PDF" "$RANGE_LIST_INPUT" -- "${OUT}"
191 else
192 for PAGE in "${RANGE_LIST_EXPANDED[@]}"; do
193 OUT="${FILE}_$PAGE.pdf"
194 qpdf --empty --pages "$FILE_PDF" "$PAGE" -- "${OUT}"
195 done
196 fi
197 done
Hint: The usage heredoc needs to be formatted with leading tabs.
Make file executable
1 chmod a+x pdf_split_pages.sh
Usage
Merge PDFs or pages
Simply merge some files (in a certain order).
Merge certain pages of pdf files (in a certain order).
Merge first pages of pdf files (in a alphabetic order).
Decrypt PDF
The shell function pask, that asks for a password.
oneliners#Ask_for_password