Bash Script to scale and/or resize PDFs from the command line.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 

991 řádky
27 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. ###################################################
  12. # PAGESIZE LOGIC
  13. # 1- Try to get Mediabox with CAT/GREP
  14. # 2- MacOS => try to use mdls
  15. # 3- Try to use pdfinfo
  16. # 4- Try to use identify (imagemagick)
  17. # 5- Fail
  18. ###################################################
  19. VERSION="2.0.0"
  20. SCALE="0.95" # scaling factor (0.95 = 95%, e.g.)
  21. VERBOSE=0 # verbosity Level
  22. PDFSCALE_NAME="$(basename $0)" # simplified name of this script
  23. # Set with which later
  24. GSBIN="" # GhostScript Binary
  25. BCBIN="" # BC Math Binary
  26. IDBIN="" # Identify Binary
  27. PDFINFOBIN="" # PDF Info Binary
  28. MDLSBIN="" # MacOS mdls Binary
  29. OSNAME="$(uname 2>/dev/null)" # Check where we are running
  30. LC_MEASUREMENT="C" # To make sure our numbers have .decimals
  31. LC_ALL="C" # Some languages use , as decimal token
  32. LC_CTYPE="C"
  33. LC_NUMERIC="C"
  34. TRUE=0 # Silly stuff
  35. FALSE=1
  36. ADAPTIVEMODE=$TRUE # Automatically try to guess best mode
  37. AUTOMATIC_SCALING=$TRUE # Default scaling in $SCALE, override by resize mode
  38. MODE=""
  39. RESIZE_PAPER_TYPE=""
  40. PGWIDTH=""
  41. PGHEIGHT=""
  42. RESIZE_WIDTH=""
  43. RESIZE_HEIGHT=""
  44. # Exit flags
  45. EXIT_SUCCESS=0
  46. EXIT_ERROR=1
  47. EXIT_INVALID_PAGE_SIZE_DETECTED=10
  48. EXIT_FILE_NOT_FOUND=20
  49. EXIT_INPUT_NOT_PDF=21
  50. EXIT_INVALID_OPTION=22
  51. EXIT_NO_INPUT_FILE=23
  52. EXIT_INVALID_SCALE=24
  53. EXIT_MISSING_DEPENDENCY=25
  54. EXIT_IMAGEMAGIK_NOT_FOUND=26
  55. EXIT_MAC_MDLS_NOT_FOUND=27
  56. EXIT_PDFINFO_NOT_FOUND=28
  57. EXIT_INVALID_PAPER_SIZE=50
  58. # Parses and validates the scaling factor
  59. parseScale() {
  60. AUTOMATIC_SCALING=$FALSE
  61. if ! [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ && (($1 > 0 )) ]] ; then
  62. printError "Invalid factor: $1"
  63. printError "The factor must be a floating point number greater than 0"
  64. printError "Example: for 80% use 0.8"
  65. exit $EXIT_INVALID_SCALE
  66. fi
  67. SCALE=$1
  68. }
  69. # Parse a forced mode of operation
  70. parseMode() {
  71. if [[ -z $1 ]]; then
  72. printError "Mode is empty, please specify the desired mode"
  73. printError "Falling back to adaptive mode!"
  74. ADAPTIVEMODE=$TRUE
  75. MODE=""
  76. return $FALSE
  77. fi
  78. if [[ $1 = 'c' || $1 = 'catgrep' || $1 = 'cat+grep' || $1 = 'CatGrep' || $1 = 'C' || $1 = 'CATGREP' ]]; then
  79. ADAPTIVEMODE=$FALSE
  80. MODE="CATGREP"
  81. return $TRUE
  82. elif [[ $1 = 'i' || $1 = 'imagemagick' || $1 = 'identify' || $1 = 'ImageMagick' || $1 = 'Identify' || $1 = 'I' || $1 = 'IDENTIFY' ]]; then
  83. ADAPTIVEMODE=$FALSE
  84. MODE="IDENTIFY"
  85. return $TRUE
  86. elif [[ $1 = 'm' || $1 = 'mdls' || $1 = 'MDLS' || $1 = 'quartz' || $1 = 'mac' || $1 = 'M' ]]; then
  87. ADAPTIVEMODE=$FALSE
  88. MODE="MDLS"
  89. return $TRUE
  90. elif [[ $1 = 'p' || $1 = 'pdfinfo' || $1 = 'PDFINFO' || $1 = 'PdfInfo' || $1 = 'P' ]]; then
  91. ADAPTIVEMODE=$FALSE
  92. MODE="PDFINFO"
  93. return $TRUE
  94. elif [[ $1 = 'a' || $1 = 'adaptive' || $1 = 'automatic' || $1 = 'A' || $1 = 'ADAPTIVE' || $1 = 'AUTOMATIC' ]]; then
  95. ADAPTIVEMODE=$TRUE
  96. MODE=""
  97. return $TRUE
  98. else
  99. printError "Invalid mode: $1"
  100. printError "Falling back to adaptive mode!"
  101. ADAPTIVEMODE=$TRUE
  102. MODE=""
  103. return $FALSE
  104. fi
  105. return $FALSE
  106. }
  107. # Gets page size using imagemagick's identify
  108. getPageSizeImagemagick() {
  109. # Sanity and Adaptive together
  110. if notIsFile "$IDBIN" && isNotAdaptiveMode; then
  111. notAdaptiveFailed "Make sure you installed ImageMagick and have identify on your \$PATH" "ImageMagick's Identify"
  112. elif notIsFile "$IDBIN" && isAdaptiveMode; then
  113. return $FALSE
  114. fi
  115. # get data from image magick
  116. local identify="$("$IDBIN" -format '%[fx:w] %[fx:h]BREAKME' "$INFILEPDF" 2>/dev/null)"
  117. if isEmpty "$identify" && isNotAdaptiveMode; then
  118. notAdaptiveFailed "ImageMagicks's Identify returned an empty string!"
  119. elif isEmpty "$identify" && isAdaptiveMode; then
  120. return $FALSE
  121. fi
  122. identify="${identify%%BREAKME*}" # get page size only for 1st page
  123. identify=($identify) # make it an array
  124. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  125. PGHEIGHT=$(printf '%.0f' "${identify[1]}") # assign
  126. return $TRUE
  127. }
  128. # Gets page size using Mac Quarts mdls
  129. getPageSizeMdls() {
  130. # Sanity and Adaptive together
  131. if notIsFile "$MDLSBIN" && isNotAdaptiveMode; then
  132. notAdaptiveFailed "Are you even trying this on a Mac?" "Mac Quartz mdls"
  133. elif notIsFile "$MDLSBIN" && isAdaptiveMode; then
  134. return $FALSE
  135. fi
  136. local identify="$("$MDLSBIN" -mdls -name kMDItemPageHeight -name kMDItemPageWidth "$INFILEPDF" 2>/dev/null)"
  137. if isEmpty "$identify" && isNotAdaptiveMode; then
  138. notAdaptiveFailed "Mac Quartz mdls returned an empty string!"
  139. elif isEmpty "$identify" && isAdaptiveMode; then
  140. return $FALSE
  141. fi
  142. identify=${identify//$'\t'/ } # change tab to space
  143. identify=($identify) # make it an array
  144. if [[ "${identify[5]}" = "(null)" || "${identify[5]}" = "(null)" ]] && isNotAdaptiveMode; then
  145. notAdaptiveFailed "There was no metadata to read from the file! Is Spotlight OFF?"
  146. elif [[ "${identify[5]}" = "(null)" || "${identify[5]}" = "(null)" ]] && isAdaptiveMode; then
  147. return $FALSE
  148. fi
  149. PGWIDTH=$(printf '%.0f' "${identify[5]}") # assign
  150. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  151. return $TRUE
  152. }
  153. # Gets page size using Linux PdfInfo
  154. getPageSizePdfInfo() {
  155. # Sanity and Adaptive together
  156. if notIsFile "$PDFINFOBIN" && isNotAdaptiveMode; then
  157. notAdaptiveFailed "Do you have pdfinfo installed and available on your \$PATH?" "Linux pdfinfo"
  158. elif notIsFile "$PDFINFOBIN" && isAdaptiveMode; then
  159. return $FALSE
  160. fi
  161. # get data from image magick
  162. local identify="$("$PDFINFOBIN" "$INFILEPDF" 2>/dev/null | grep -i 'Page size:' )"
  163. if isEmpty "$identify" && isNotAdaptiveMode; then
  164. notAdaptiveFailed "Linux PdfInfo returned an empty string!"
  165. elif isEmpty "$identify" && isAdaptiveMode; then
  166. return $FALSE
  167. fi
  168. identify="${identify##*Page size:}" # remove stuff
  169. identify=($identify) # make it an array
  170. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  171. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  172. return $TRUE
  173. }
  174. # Gets page size using cat and grep
  175. getPageSizeCatGrep() {
  176. # get MediaBox info from PDF file using cat and grep, these are all possible
  177. # /MediaBox [0 0 595 841]
  178. # /MediaBox [ 0 0 595.28 841.89]
  179. # /MediaBox[ 0 0 595.28 841.89 ]
  180. # Get MediaBox data if possible
  181. local mediaBox="$(cat "$INFILEPDF" | grep -a '/MediaBox' | head -n1)"
  182. mediaBox="${mediaBox##*/MediaBox}"
  183. # No page size data available
  184. if isEmpty "$mediaBox" && isNotAdaptiveMode; then
  185. notAdaptiveFailed "There is no MediaBox in the pdf document!"
  186. elif isEmpty "$mediaBox" && isAdaptiveMode; then
  187. return $FALSE
  188. fi
  189. # remove chars [ and ]
  190. mediaBox="${mediaBox//[}"
  191. mediaBox="${mediaBox//]}"
  192. mediaBox=($mediaBox) # make it an array
  193. mbCount=${#mediaBox[@]} # array size
  194. # sanity
  195. if [[ $mbCount -lt 4 ]]; then
  196. printError "Error when reading the page size!"
  197. printError "The page size information is invalid!"
  198. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  199. fi
  200. # we are done
  201. PGWIDTH=$(printf '%.0f' "${mediaBox[2]}") # Get Round Width
  202. PGHEIGHT=$(printf '%.0f' "${mediaBox[3]}") # Get Round Height
  203. return $TRUE
  204. }
  205. # Prints error message and exits execution
  206. notAdaptiveFailed() {
  207. local errProgram="$2"
  208. local errStr="$1"
  209. if isEmpty "$2"; then
  210. printError "Error when reading input file!"
  211. printError "Could not determine the page size!"
  212. else
  213. printError "Error! $2 was not found!"
  214. fi
  215. isNotEmpty "$errStr" && printError "$errStr"
  216. printError "Aborting! You may want to try the adaptive mode."
  217. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  218. }
  219. # Return $TRUE if adaptive mode is enabled, false otherwise
  220. isAdaptiveMode() {
  221. return $ADAPTIVEMODE
  222. }
  223. # Return $TRUE if adaptive mode is disabled, false otherwise
  224. isNotAdaptiveMode() {
  225. isAdaptiveMode && return $FALSE
  226. return $TRUE
  227. }
  228. # Return $TRUE if $1 is empty, false otherwise
  229. isEmpty() {
  230. [[ -z "$1" ]] && return $TRUE
  231. return $FALSE
  232. }
  233. # Return $TRUE if $1 is NOT empty, false otherwise
  234. isNotEmpty() {
  235. [[ -z "$1" ]] && return $FALSE
  236. return $TRUE
  237. }
  238. # Detects operation mode and also runs the adaptive mode
  239. getPageSize() {
  240. if isNotAdaptiveMode; then
  241. vprint " Get Page Size: Adaptive Disabled"
  242. if [[ $MODE = "CATGREP" ]]; then
  243. vprint " Method: Cat + Grep"
  244. getPageSizeCatGrep
  245. elif [[ $MODE = "MDLS" ]]; then
  246. vprint " Method: Mac Quartz mdls"
  247. getPageSizeMdls
  248. elif [[ $MODE = "PDFINFO" ]]; then
  249. vprint " Method: PDFInfo"
  250. getPageSizePdfInfo
  251. elif [[ $MODE = "IDENTIFY" ]]; then
  252. vprint " Method: ImageMagick's Identify"
  253. getPageSizeImagemagick
  254. else
  255. printError "Error! Invalid Mode: $MODE"
  256. printError "Aborting execution..."
  257. exit $EXIT_INVALID_OPTION
  258. fi
  259. return $TRUE
  260. fi
  261. vprint " Get Page Size: Adaptive Enabled"
  262. vprint " Method: Cat + Grep"
  263. getPageSizeCatGrep
  264. if pageSizeIsInvalid && [[ $OSNAME = "Darwin" ]]; then
  265. vprint " Failed"
  266. vprint " Method: Mac Quartz mdls"
  267. getPageSizeMdls
  268. fi
  269. if pageSizeIsInvalid; then
  270. vprint " Failed"
  271. vprint " Method: PDFInfo"
  272. getPageSizePdfInfo
  273. fi
  274. if pageSizeIsInvalid; then
  275. vprint " Failed"
  276. vprint " Method: ImageMagick's Identify"
  277. getPageSizeImagemagick
  278. fi
  279. if pageSizeIsInvalid; then
  280. vprint " Failed"
  281. printError "Error when detecting PDF paper size!"
  282. printError "All methods of detection failed"
  283. printError "You may want to install pdfinfo or imagemagick"
  284. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  285. fi
  286. return $TRUE
  287. }
  288. vPrintSourcePageSizes() {
  289. vprint " $1 Width: $PGWIDTH postscript-points"
  290. vprint "$1 Height: $PGHEIGHT postscript-points"
  291. }
  292. # Returns $TRUE if $PGWIDTH OR $PGWIDTH are empty or NOT an Integer, false otherwise
  293. pageSizeIsInvalid() {
  294. if isNotAnInteger "$PGWIDTH" || isNotAnInteger "$PGHEIGHT"; then
  295. return $TRUE
  296. fi
  297. return $FALSE
  298. }
  299. isAnInteger() {
  300. case $1 in
  301. ''|*[!0-9]*) return $FALSE ;;
  302. *) return $TRUE ;;
  303. esac
  304. }
  305. isNotAnInteger() {
  306. case $1 in
  307. ''|*[!0-9]*) return $TRUE ;;
  308. *) return $FALSE ;;
  309. esac
  310. }
  311. # Prints usage info
  312. usage() {
  313. [[ "$2" != 'nobanner' ]] && printVersion 2
  314. [[ ! -z "$1" ]] && printError "$1"
  315. printError "Usage: $PDFSCALE_NAME [-v] [-s <factor>] [-m <mode>] <inFile.pdf> [outfile.pdf]"
  316. printError "Try: $PDFSCALE_NAME -h # for help"
  317. }
  318. # Prints Verbose information
  319. vprint() {
  320. [[ $VERBOSE -eq 0 ]] && return 0
  321. timestamp=""
  322. [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | "
  323. echo "$timestamp$1"
  324. }
  325. # Prints dependency information and aborts execution
  326. printDependency() {
  327. #printVersion 2
  328. local brewName="$1"
  329. [[ "$1" = 'pdfinfo' && "$OSNAME" = "Darwin" ]] && brewName="xpdf"
  330. printError $'\n'"ERROR! You need to install the package '$1'"$'\n'
  331. printError "Linux apt-get.: sudo apt-get install $1"
  332. printError "Linux yum.....: sudo yum install $1"
  333. printError "MacOS homebrew: brew install $brewName"
  334. printError $'\n'"Aborting..."
  335. exit $EXIT_MISSING_DEPENDENCY
  336. }
  337. # Prints initialization errors and aborts execution
  338. initError() {
  339. local errStr="$1"
  340. local exitStat=$2
  341. [[ -z "$exitStat" ]] && exitStat=$EXIT_ERROR
  342. usage "ERROR! $errStr" "$3"
  343. exit $exitStat
  344. }
  345. # Prints to stderr
  346. printError() {
  347. echo >&2 "$@"
  348. }
  349. # Returns $TRUE if $1 has a .pdf extension, false otherwsie
  350. isPDF() {
  351. [[ "$1" =~ ^..*\.pdf$ ]] && return $TRUE
  352. return $FALSE
  353. }
  354. # Returns $TRUE if $1 is a file, false otherwsie
  355. isFile() {
  356. [[ -f "$1" ]] && return $TRUE
  357. return $FALSE
  358. }
  359. # Returns $TRUE if $1 is NOT a file, false otherwsie
  360. notIsFile() {
  361. [[ -f "$1" ]] && return $FALSE
  362. return $TRUE
  363. }
  364. # Returns $TRUE if $1 is executable, false otherwsie
  365. isExecutable() {
  366. [[ -x "$1" ]] && return $TRUE
  367. return $FALSE
  368. }
  369. # Returns $TRUE if $1 is NOT executable, false otherwsie
  370. notIsExecutable() {
  371. [[ -x "$1" ]] && return $FALSE
  372. return $TRUE
  373. }
  374. # Returns $TRUE if $1 is a file and executable, false otherwsie
  375. isAvailable() {
  376. if isFile "$1" && isExecutable "$1"; then
  377. return $TRUE
  378. fi
  379. return $FALSE
  380. }
  381. # Returns $TRUE if $1 is NOT a file or NOT executable, false otherwsie
  382. notIsAvailable() {
  383. if notIsFile "$1" || notIsExecutable "$1"; then
  384. return $TRUE
  385. fi
  386. return $FALSE
  387. }
  388. # Loads external dependencies and checks for errors
  389. loadDeps() {
  390. GSBIN="$(which gs 2>/dev/null)"
  391. BCBIN="$(which bc 2>/dev/null)"
  392. IDBIN=$(which identify 2>/dev/null)
  393. MDLSBIN="$(which mdls 2>/dev/null)"
  394. PDFINFOBIN="$(which pdfinfo 2>/dev/null)"
  395. vprint "Checking for ghostscript and bcmath"
  396. if notIsAvailable "$GSBIN"; then printDependency 'ghostscript'; fi
  397. if notIsAvailable "$BCBIN"; then printDependency 'bc'; fi
  398. if [[ $MODE = "IDENTIFY" ]]; then
  399. vprint "Checking for imagemagick's identify"
  400. if notIsAvailable "$IDBIN"; then printDependency 'imagemagick'; fi
  401. fi
  402. if [[ $MODE = "PDFINFO" ]]; then
  403. vprint "Checking for pdfinfo"
  404. if notIsAvailable "$PDFINFOBIN"; then printDependency 'pdfinfo'; fi
  405. fi
  406. if [[ $MODE = "MDLS" ]]; then
  407. vprint "Checking for MacOS mdls"
  408. if notIsAvailable "$MDLSBIN"; then
  409. initError 'mdls executable was not found! Is this even MacOS?' $EXIT_MAC_MDLS_NOT_FOUND 'nobanner'
  410. fi
  411. fi
  412. }
  413. # Main execution
  414. main() {
  415. printVersion 1 'verbose'
  416. #getScaledOutputName
  417. #Intro message
  418. #vprint "$(basename $0) v$VERSION - Verbose execution"
  419. loadDeps
  420. vprint " Input file: $INFILEPDF"
  421. vprint " Output file: $OUTFILEPDF"
  422. getPageSize
  423. if isMixedMode; then
  424. vprint " Mixed Tasks: Resize & Scale"
  425. vprint " Scale factor: $SCALE"
  426. vPrintSourcePageSizes ' Source'
  427. outputFile="$OUTFILEPDF" # backup outFile name
  428. tempFile="${OUTFILEPDF%.pdf}.__TEMP__.pdf" # set a temp file name
  429. OUTFILEPDF="$tempFile" # set output to tmp file
  430. pageResize # resize to tmp file
  431. INFILEPDF="$tempFile" # get tmp file as input
  432. OUTFILEPDF="$outputFile" # reset final target
  433. PGWIDTH=$RESIZE_WIDTH # we already know the new page size
  434. PGHEIGHT=$RESIZE_HEIGHT # from the last command (Resize)
  435. vPrintSourcePageSizes ' New'
  436. pageScale # scale the resized pdf
  437. # remove tmp file
  438. rm "$tempFile" >/dev/null 2>&1 || printError "Error when removing temporary file: $tempFile"
  439. elif isResizeMode; then
  440. vprint " Single Task: Resize PDF Paper"
  441. vprint " Scale factor: Disabled (resize only)"
  442. vPrintSourcePageSizes ' Source'
  443. pageResize
  444. else
  445. local scaleMode=""
  446. vprint " Single Task: Scale PDF Contents"
  447. isManualScaledMode && scaleMode='(manual)' || scaleMode='(auto)'
  448. vprint " Scale factor: $SCALE $scaleMode"
  449. vPrintSourcePageSizes ' Source'
  450. pageScale
  451. fi
  452. #pageScale
  453. #pageResize
  454. }
  455. # Parse options
  456. getOptions() {
  457. while getopts ":vhVs:m:r:p" o; do
  458. case "${o}" in
  459. v)
  460. ((VERBOSE++))
  461. ;;
  462. h)
  463. printHelp
  464. exit $EXIT_SUCCESS
  465. ;;
  466. V)
  467. printVersion
  468. exit $EXIT_SUCCESS
  469. ;;
  470. s)
  471. parseScale ${OPTARG}
  472. ;;
  473. m)
  474. parseMode ${OPTARG}
  475. ;;
  476. r)
  477. parsePaperResize ${OPTARG}
  478. ;;
  479. p)
  480. printPaperInfo
  481. exit $EXIT_SUCCESS
  482. ;;
  483. *)
  484. initError "Invalid Option: -$OPTARG" $EXIT_INVALID_OPTION
  485. ;;
  486. esac
  487. done
  488. shift $((OPTIND-1))
  489. # Validate input PDF file
  490. INFILEPDF="$1"
  491. isEmpty "$INFILEPDF" && initError "Input file is empty!" $EXIT_NO_INPUT_FILE
  492. isPDF "$INFILEPDF" || initError "Input file is not a PDF file: $INFILEPDF" $EXIT_INPUT_NOT_PDF
  493. isFile "$INFILEPDF" || initError "Input file not found: $INFILEPDF" $EXIT_FILE_NOT_FOUND
  494. if isEmpty "$2"; then
  495. if isMixedMode; then
  496. OUTFILEPDF="${INFILEPDF%.pdf}.$(uppercase $RESIZE_PAPER_TYPE).SCALED.pdf"
  497. elif isResizeMode; then
  498. OUTFILEPDF="${INFILEPDF%.pdf}.$(uppercase $RESIZE_PAPER_TYPE).pdf"
  499. else
  500. OUTFILEPDF="${INFILEPDF%.pdf}.SCALED.pdf"
  501. fi
  502. else
  503. OUTFILEPDF="${2%.pdf}.pdf"
  504. fi
  505. }
  506. # Runs the ghostscript scaling script
  507. pageScale() {
  508. # Compute translation factors (to center page).
  509. XTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN")
  510. YTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN")
  511. vprint " Translation X: $XTRANS"
  512. vprint " Translation Y: $YTRANS"
  513. # Do it.
  514. "$GSBIN" \
  515. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  516. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  517. -dColorConversionStrategy=/LeaveColorUnchanged \
  518. -dSubsetFonts=true -dEmbedAllFonts=true \
  519. -dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \
  520. -sOutputFile="$OUTFILEPDF" \
  521. -c "<</BeginPage{$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
  522. -f "$INFILEPDF" &
  523. wait ${!}
  524. }
  525. pageResize() {
  526. getGSPaperSize "$RESIZE_PAPER_TYPE"
  527. local tmpInverter=""
  528. if [[ $PGWIDTH -gt $PGHEIGHT && $RESIZE_WIDTH -lt $RESIZE_HEIGHT ]]; then
  529. vprint " Flip Detect: Wrong orientation!"
  530. vprint " Inverting Width <-> Height"
  531. tmpInverter=$RESIZE_HEIGHT
  532. RESIZE_HEIGHT=$RESIZE_WIDTH
  533. RESIZE_WIDTH=$tmpInverter
  534. fi
  535. vprint " Resizing to: $(uppercase $RESIZE_PAPER_TYPE) ( $RESIZE_WIDTH x $RESIZE_HEIGHT )"
  536. # Change page size
  537. "$GSBIN" \
  538. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  539. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  540. -dColorConversionStrategy=/LeaveColorUnchanged \
  541. -dSubsetFonts=true -dEmbedAllFonts=true \
  542. -dDEVICEWIDTHPOINTS=$RESIZE_WIDTH -dDEVICEHEIGHTPOINTS=$RESIZE_HEIGHT \
  543. -dFIXEDMEDIA -dPDFFitPage \
  544. -sOutputFile="$OUTFILEPDF" \
  545. -f "$INFILEPDF" &
  546. wait ${!}
  547. }
  548. getPaperInfo() {
  549. # name inchesW inchesH mmW mmH pointsW pointsH
  550. sizesUS="\
  551. 11x17 11.0 17.0 279 432 792 1224
  552. ledger 17.0 11.0 432 279 1224 792
  553. legal 8.5 14.0 216 356 612 1008
  554. letter 8.5 11.0 216 279 612 792
  555. lettersmall 8.5 11.0 216 279 612 792
  556. archE 36.0 48.0 914 1219 2592 3456
  557. archD 24.0 36.0 610 914 1728 2592
  558. archC 18.0 24.0 457 610 1296 1728
  559. archB 12.0 18.0 305 457 864 1296
  560. archA 9.0 12.0 229 305 648 864"
  561. sizesISO="\
  562. a0 33.1 46.8 841 1189 2384 3370
  563. a1 23.4 33.1 594 841 1684 2384
  564. a2 16.5 23.4 420 594 1191 1684
  565. a3 11.7 16.5 297 420 842 1191
  566. a4 8.3 11.7 210 297 595 842
  567. a4small 8.3 11.7 210 297 595 842
  568. a5 5.8 8.3 148 210 420 595
  569. a6 4.1 5.8 105 148 297 420
  570. a7 2.9 4.1 74 105 210 297
  571. a8 2.1 2.9 52 74 148 210
  572. a9 1.5 2.1 37 52 105 148
  573. a10 1.0 1.5 26 37 73 105
  574. isob0 39.4 55.7 1000 1414 2835 4008
  575. isob1 27.8 39.4 707 1000 2004 2835
  576. isob2 19.7 27.8 500 707 1417 2004
  577. isob3 13.9 19.7 353 500 1001 1417
  578. isob4 9.8 13.9 250 353 709 1001
  579. isob5 6.9 9.8 176 250 499 709
  580. isob6 4.9 6.9 125 176 354 499
  581. c0 36.1 51.1 917 1297 2599 3677
  582. c1 25.5 36.1 648 917 1837 2599
  583. c2 18.0 25.5 458 648 1298 1837
  584. c3 12.8 18.0 324 458 918 1298
  585. c4 9.0 12.8 229 324 649 918
  586. c5 6.4 9.0 162 229 459 649
  587. c6 4.5 6.4 114 162 323 459"
  588. sizesJIS="\
  589. jisb0 NA NA 1030 1456 2920 4127
  590. jisb1 NA NA 728 1030 2064 2920
  591. jisb2 NA NA 515 728 1460 2064
  592. jisb3 NA NA 364 515 1032 1460
  593. jisb4 NA NA 257 364 729 1032
  594. jisb5 NA NA 182 257 516 729
  595. jisb6 NA NA 128 182 363 516"
  596. sizesOther="\
  597. flsa 8.5 13.0 216 330 612 936
  598. flse 8.5 13.0 216 330 612 936
  599. halfletter 5.5 8.5 140 216 396 612
  600. hagaki 3.9 5.8 100 148 283 420"
  601. sizesAll="\
  602. $sizesUS
  603. $sizesISO
  604. $sizesJIS
  605. $sizesOther"
  606. }
  607. getGSPaperSize() {
  608. isEmpty "$sizesall" && getPaperInfo
  609. while read l; do
  610. local cols=($l)
  611. if [[ "$1" == ${cols[0]} ]]; then
  612. RESIZE_WIDTH=${cols[5]}
  613. RESIZE_HEIGHT=${cols[6]}
  614. return $TRUE
  615. fi
  616. done <<< "$sizesAll"
  617. }
  618. getPaperNames() {
  619. paperNames=(a0 a1 a2 a3 a4 a4small a5 a6 a7 a8 a9 a10 isob0 isob1 isob2 isob3 isob4 isob5 isob6 c0 c1 c2 c3 c4 c5 c6 \
  620. 11x17 ledger legal letter lettersmall archE archD archC archB archA \
  621. jisb0 jisb1 jisb2 jisb3 jisb4 jisb5 jisb6 \
  622. flsa flse halfletter hagaki)
  623. }
  624. printPaperNames() {
  625. isEmpty "$paperNames" && getPaperNames
  626. for i in "${!paperNames[@]}"; do
  627. [[ $i -ne 0 && $((i % 5)) -eq 0 ]] && echo ""
  628. ppN="$(uppercase ${paperNames[i]})"
  629. printf "%-14s" "$ppN"
  630. done
  631. echo ""
  632. }
  633. isPaperName() {
  634. isEmpty "$1" && return $FALSE
  635. isEmpty "$paperNames" && getPaperNames
  636. for i in "${paperNames[@]}"; do
  637. [[ "$i" = "$1" ]] && return $TRUE
  638. done
  639. return $FALSE
  640. }
  641. printPaperInfo() {
  642. printVersion
  643. echo $'\n'"Valid Ghostscript Paper Sizes accepted"$'\n'
  644. getPaperInfo
  645. printPaperTable "ISO STANDARD" "$sizesISO"; echo
  646. printPaperTable "US STANDARD" "$sizesUS"; echo
  647. printPaperTable "JIS STANDARD *Aproximated Points" "$sizesJIS"; echo
  648. printPaperTable "OTHERS" "$sizesOther"; echo
  649. }
  650. printTableLine() {
  651. echo '+-----------------------------------------------------------------+'
  652. }
  653. printTableDivider() {
  654. echo '+-----------------+-------+-------+-------+-------+-------+-------+'
  655. }
  656. printTableHeader() {
  657. echo '| Name | inchW | inchH | mm W | mm H | pts W | pts H |'
  658. }
  659. printTableTitle() {
  660. printf "| %-64s%s\n" "$1" '|'
  661. }
  662. printPaperTable() {
  663. printTableLine
  664. printTableTitle "$1"
  665. printTableLine
  666. printTableHeader
  667. printTableDivider
  668. while read l; do
  669. local cols=($l)
  670. printf "| %-15s | %+5s | %+5s | %+5s | %+5s | %+5s | %+5s |\n" ${cols[*]};
  671. done <<< "$2"
  672. printTableDivider
  673. }
  674. parsePaperResize() {
  675. isEmpty "$1" && initError 'Invalid Paper Type: (empty)' $EXIT_INVALID_PAPER_SIZE
  676. local lowercasePaper="$(lowercase $1)"
  677. ! isPaperName "$lowercasePaper" && initError "Invalid Paper Type: $1" $EXIT_INVALID_PAPER_SIZE
  678. RESIZE_PAPER_TYPE="$lowercasePaper"
  679. }
  680. isManualScaledMode() {
  681. [[ $AUTOMATIC_SCALING -eq $TRUE ]] && return $FALSE
  682. return $TRUE
  683. }
  684. isResizeMode() {
  685. isEmpty $RESIZE_PAPER_TYPE && return $FALSE
  686. return $TRUE
  687. }
  688. isMixedMode() {
  689. isResizeMode && isManualScaledMode && return $TRUE
  690. return $FALSE
  691. }
  692. mmToPoints() {
  693. local calc=$(($1))
  694. }
  695. lowercaseChar() {
  696. case "$1" in
  697. [A-Z])
  698. n=$(printf "%d" "'$1")
  699. n=$((n+32))
  700. printf \\$(printf "%o" "$n")
  701. ;;
  702. *)
  703. printf "%s" "$1"
  704. ;;
  705. esac
  706. }
  707. lowercase() {
  708. word="$@"
  709. for((i=0;i<${#word};i++))
  710. do
  711. ch="${word:$i:1}"
  712. lowercaseChar "$ch"
  713. done
  714. }
  715. uppercaseChar(){
  716. case "$1" in
  717. [a-z])
  718. n=$(printf "%d" "'$1")
  719. n=$((n-32))
  720. printf \\$(printf "%o" "$n")
  721. ;;
  722. *)
  723. printf "%s" "$1"
  724. ;;
  725. esac
  726. }
  727. uppercase() {
  728. word="$@"
  729. for((i=0;i<${#word};i++))
  730. do
  731. ch="${word:$i:1}"
  732. uppercaseChar "$ch"
  733. done
  734. }
  735. #printPaperInfo
  736. #printPaperNames
  737. #echo "----"
  738. #isPaperName a4s; echo $?
  739. ####----------Print-Program-Information----------####
  740. # Prints version
  741. printVersion() {
  742. local vStr=""
  743. [[ "$2" = 'verbose' ]] && vStr=" - Verbose Execution"
  744. if [[ $1 -eq 2 ]]; then
  745. printError "$PDFSCALE_NAME v$VERSION$vStr"
  746. else
  747. echo "$PDFSCALE_NAME v$VERSION$vStr"
  748. fi
  749. }
  750. # Prints help info
  751. printHelp() {
  752. printVersion
  753. local paperList="$(printPaperNames)"
  754. echo "
  755. Usage: $PDFSCALE_NAME [-v] [-s <factor>] [-m <mode>] [-r <paper>] <inFile.pdf> [outfile.pdf]
  756. $PDFSCALE_NAME -p
  757. $PDFSCALE_NAME -h
  758. $PDFSCALE_NAME -V
  759. Parameters:
  760. -v Verbose mode, prints extra information
  761. Use twice for timestamp
  762. -h Print this help to screen and exits
  763. -V Prints version to screen and exits
  764. -m <mode> Page size Detection mode
  765. May disable the Adaptive Mode
  766. -s <factor> Changes the scaling factor or forces scaling
  767. Defaults: $SCALE / no scaling (resize mode)
  768. MUST be a number bigger than zero
  769. Eg. -s 0.8 for 80% of the original size
  770. -r <paper> Triggers the Resize Paper Mode
  771. Resize PDF paper proportionally
  772. Must be a valid Ghostscript paper name
  773. -p Prints Ghostscript paper info tables to screen
  774. Scaling Mode:
  775. The default mode of operation is scaling mode with fixed paper
  776. size and scaling pre-set to $SCALE. By not using the resize mode
  777. you are using scaling mode.
  778. Resize Paper Mode:
  779. Disables the default scaling factor! ($SCALE)
  780. Alternative mode of operation to change the PDF paper
  781. proportionally. Will fit-to-page.
  782. Mixed Mode:
  783. In mixed mode both the -s option and -r option must be specified.
  784. The PDF will be both scaled and have the paper type changed.
  785. Output filename:
  786. The output filename is optional. If no file name is passed
  787. the output file will have the same name/destination of the
  788. input file with added suffixes:
  789. .SCALED.pdf is added to scaled files
  790. .<PAPERSIZE>.pdf is added to resized files
  791. .<PAPERSIZE>.SCALED.pdf is added in mixed mode
  792. Page Detection Modes:
  793. a, adaptive Default mode, tries all the methods below
  794. c, cat+grep Forces the use of the cat + grep method
  795. m, mdls Forces the use of MacOS Quartz mdls
  796. p, pdfinfo Forces the use of PDFInfo
  797. i, identify Forces the use of ImageMagick's Identify
  798. Valid Ghostscript Paper Names:
  799. $paperList
  800. Notes:
  801. - Adaptive Page size detection will try different modes until
  802. it gets a page size. You can force a mode with -m 'mode'.
  803. - Options must be passed before the file names to be parsed.
  804. - Having the extension .pdf on the output file name is optional,
  805. it will be added if not present.
  806. - File and folder names with spaces should be quoted or escaped.
  807. - The scaling is centered and using a scale bigger than 1 may
  808. result on cropping parts of the pdf.
  809. Examples:
  810. $PDFSCALE_NAME myPdfFile.pdf
  811. $PDFSCALE_NAME myPdfFile.pdf myScaledPdf
  812. $PDFSCALE_NAME -v -v myPdfFile.pdf
  813. $PDFSCALE_NAME -s 0.85 myPdfFile.pdf myScaledPdf.pdf
  814. $PDFSCALE_NAME -m pdfinfo -s 0.80 -v myPdfFile.pdf
  815. $PDFSCALE_NAME -v -v -m i -s 0.7 myPdfFile.pdf
  816. $PDFSCALE_NAME -h
  817. "
  818. }
  819. ######### START EXECUTION
  820. getOptions "${@}"
  821. main
  822. exit $?