Bash Script to scale and/or resize PDFs from the command line.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.8 KiB

  1. #!/usr/bin/env bash
  2. # pdfScale.sh
  3. #
  4. # Scale PDF to specified percentage of original size.
  5. #
  6. # Gustavo Arnosti Neves - 2016 / 07 / 10
  7. #
  8. # This script: https://github.com/tavinus/pdfScale
  9. # Based on: http://ma.juii.net/blog/scale-page-content-of-pdf-files
  10. # And: https://gist.github.com/MichaelJCole/86e4968dbfc13256228a
  11. VERSION="1.2.10"
  12. SCALE="0.95" # scaling factor (0.95 = 95%, e.g.)
  13. VERBOSE=0 # verbosity Level
  14. BASENAME="$(basename $0)" # simplified name of this script
  15. GSBIN="" # Set with which after we check dependencies
  16. BCBIN="" # Set with which after we check dependencies
  17. IDBIN="" # Set with which after we check dependencies
  18. LC_MEASUREMENT="C" # To make sure our numbers have .decimals
  19. LC_ALL="C" # Some languages use , as decimal token
  20. LC_CTYPE="C"
  21. LC_NUMERIC="C"
  22. TRUE=0 # Silly stuff
  23. FALSE=1
  24. USEIMGMGK=$FALSE # ImageMagick Flag, will use identify if true
  25. # Prints version
  26. printVersion() {
  27. if [[ $1 -eq 2 ]]; then
  28. echo >&2 "$BASENAME v$VERSION"
  29. else
  30. echo "$BASENAME v$VERSION"
  31. fi
  32. }
  33. # Prints help info
  34. printHelp() {
  35. printVersion
  36. echo "
  37. Usage: $BASENAME [-v] [-s <factor>] [-i] <inFile.pdf> [outfile.pdf]
  38. $BASENAME -h
  39. $BASENAME -V
  40. Parameters:
  41. -v Verbose mode, prints extra information
  42. Use twice for even more information
  43. -h Print this help to screen and exits
  44. -V Prints version to screen and exits
  45. -i Use imagemagick to get page size,
  46. instead of cat + grep method
  47. -s <factor> Changes the scaling factor, defaults to 0.95
  48. MUST be a number bigger than zero.
  49. Eg. -s 0.8 for 80% of the original size
  50. Notes:
  51. - Options must be passed before the file names to be parsed
  52. - The output filename is optional. If no file name is passed
  53. the output file will have the same name/destination of the
  54. input file, with .SCALED.pdf at the end (instead of just .pdf)
  55. - Having the extension .pdf on the output file name is optional,
  56. it will be added if not present
  57. - Should handle file names with spaces without problems
  58. - The scaling is centered and using a scale bigger than 1 may
  59. result on cropping parts of the pdf.
  60. Examples:
  61. $BASENAME myPdfFile.pdf
  62. $BASENAME myPdfFile.pdf myScaledPdf
  63. $BASENAME -v -v myPdfFile.pdf
  64. $BASENAME -s 0.85 myPdfFile.pdf myScaledPdf.pdf
  65. $BASENAME -i -s 0.80 -v myPdfFile.pdf
  66. $BASENAME -v -v -s 0.7 myPdfFile.pdf
  67. $BASENAME -h
  68. "
  69. }
  70. # Prints usage info
  71. usage() {
  72. printVersion 2
  73. echo >&2 "Usage: $BASENAME [-v] [-s <factor>] <inFile.pdf> [outfile.pdf]"
  74. echo >&2 "Try: $BASENAME -h # for help"
  75. exit 1
  76. }
  77. # Prints Verbose information
  78. vprint() {
  79. [[ $VERBOSE -eq 0 ]] && return 0
  80. timestamp=""
  81. [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | "
  82. echo "$timestamp$1"
  83. }
  84. # Prints dependency information and aborts execution
  85. printDependency() {
  86. printVersion 2
  87. echo >&2 $'\n'"ERROR! You need to install the package '$1'"$'\n'
  88. echo >&2 "Linux apt-get.: sudo apt-get install $1"
  89. echo >&2 "Linux yum.....: sudo yum install $1"
  90. echo >&2 "MacOS homebrew: brew install $1"
  91. echo >&2 $'\n'"Aborting..."
  92. exit 3
  93. }
  94. # Parses and validates the scaling factor
  95. parseScale() {
  96. if ! [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ && (($1 > 0 )) ]] ; then
  97. echo >&2 "Invalid factor: $1"
  98. echo >&2 "The factor must be a floating point number greater than 0"
  99. echo >&2 "Example: for 80% use 0.8"
  100. exit 2
  101. fi
  102. SCALE=$1
  103. }
  104. # Gets page size using imagemagick's identify
  105. getPageSizeImagemagick() {
  106. # get data from image magick
  107. local identify="$("$IDBIN" -format '%[fx:w] %[fx:h]BREAKME' "$INFILEPDF" 2>/dev/null)"
  108. identify="${identify%%BREAKME*}" # get page size only for 1st page
  109. identify=($identify) # make it an array
  110. PGWIDTH=${identify[0]} # assign
  111. PGHEIGHT=${identify[1]}
  112. }
  113. # Gets page size using cat and grep
  114. getPageSize() {
  115. # get MediaBox info from PDF file using cat and grep, these are all possible
  116. # /MediaBox [0 0 595 841]
  117. # /MediaBox [ 0 0 595.28 841.89]
  118. # /MediaBox[ 0 0 595.28 841.89 ]
  119. # Get MediaBox data if possible
  120. local mediaBox="$(cat "$INFILEPDF" | grep -a '/MediaBox' | head -n1)"
  121. mediaBox="${mediaBox##*/MediaBox}"
  122. # If no MediaBox, try BBox
  123. if [[ -z $mediaBox ]]; then
  124. mediaBox="$(cat "$INFILEPDF" | grep -a '/BBox' | head -n1)"
  125. mediaBox="${mediaBox##*/BBox}"
  126. fi
  127. # No page size data available
  128. if [[ -z $mediaBox ]]; then
  129. echo "Error when reading input file!"
  130. echo "Could not determine the page size!"
  131. echo "There is no MediaBox or BBox in the pdf document!"
  132. echo "Aborting..."
  133. exit 15
  134. fi
  135. # remove chars [ and ]
  136. mediaBox="${mediaBox//[}"
  137. mediaBox="${mediaBox//]}"
  138. mediaBox=($mediaBox) # make it an array
  139. mbCount=${#mediaBox[@]} # array size
  140. # sanity
  141. if [[ $mbCount -lt 4 ]]; then
  142. echo "Error when reading the page size!"
  143. echo "The page size information is invalid!"
  144. exit 16
  145. fi
  146. # we are done
  147. PGWIDTH=$(printf '%.0f' "${mediaBox[2]}") # Get Round Width
  148. PGHEIGHT=$(printf '%.0f' "${mediaBox[3]}") # Get Round Height
  149. }
  150. # Parse options
  151. while getopts ":vihVs:" o; do
  152. case "${o}" in
  153. v)
  154. ((VERBOSE++))
  155. ;;
  156. h)
  157. printHelp
  158. exit 0
  159. ;;
  160. V)
  161. printVersion
  162. exit 0
  163. ;;
  164. s)
  165. parseScale ${OPTARG}
  166. ;;
  167. i)
  168. USEIMGMGK=$TRUE
  169. ;;
  170. *)
  171. usage
  172. ;;
  173. esac
  174. done
  175. shift $((OPTIND-1))
  176. ######### START EXECUTION
  177. #Intro message
  178. vprint "$(basename $0) v$VERSION - Verbose execution"
  179. # Dependencies
  180. vprint "Checking for ghostscript and bcmath"
  181. command -v gs >/dev/null 2>&1 || printDependency 'ghostscript'
  182. command -v bc >/dev/null 2>&1 || printDependency 'bc'
  183. if [[ $USEIMGMGK -eq $TRUE ]]; then
  184. vprint "Checking for imagemagick's identify"
  185. command -v identify >/dev/null 2>&1 || printDependency 'imagemagick'
  186. IDBIN=$(which identify 2>/dev/null)
  187. fi
  188. # Get dependency binaries
  189. GSBIN=$(which gs 2>/dev/null)
  190. BCBIN=$(which bc 2>/dev/null)
  191. # Verbose scale info
  192. vprint " Scale factor: $SCALE"
  193. # Validate args
  194. [[ $# -lt 1 ]] && { usage; exit 1; }
  195. INFILEPDF="$1"
  196. [[ "$INFILEPDF" =~ ^..*\.pdf$ ]] || { usage; exit 2; }
  197. vprint " Input file: $INFILEPDF"
  198. # Parse output filename
  199. if [[ -z $2 ]]; then
  200. OUTFILEPDF="${INFILEPDF%.pdf}.SCALED.pdf"
  201. else
  202. OUTFILEPDF="${2%.pdf}.pdf"
  203. fi
  204. vprint " Output file: $OUTFILEPDF"
  205. # Set PGWIDTH and PGHEIGHT
  206. if [[ $USEIMGMGK -eq $TRUE ]]; then
  207. getPageSizeImagemagick
  208. else
  209. getPageSize
  210. fi
  211. vprint " Width: $PGWIDTH postscript-points"
  212. vprint " Height: $PGHEIGHT postscript-points"
  213. # Compute translation factors (to center page.
  214. XTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN")
  215. YTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN")
  216. vprint " Translation X: $XTRANS"
  217. vprint " Translation Y: $YTRANS"
  218. # Do it.
  219. "$GSBIN" \
  220. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  221. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  222. -dColorConversionStrategy=/LeaveColorUnchanged \
  223. -dSubsetFonts=true -dEmbedAllFonts=true \
  224. -dDEVICEWIDTH=$PGWIDTH -dDEVICEHEIGHT=$PGHEIGHT \
  225. -sOutputFile="$OUTFILEPDF" \
  226. -c "<</BeginPage{$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
  227. -f "$INFILEPDF"