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.
 
 

1421 lines
49 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. # Latest Version - 2017 / 05 / 19
  8. #
  9. # This script: https://github.com/tavinus/pdfScale
  10. # Based on: http://ma.juii.net/blog/scale-page-content-of-pdf-files
  11. # And: https://gist.github.com/MichaelJCole/86e4968dbfc13256228a
  12. VERSION="2.1.2"
  13. ###################### EXTERNAL PROGRAMS #######################
  14. GSBIN="" # GhostScript Binary
  15. BCBIN="" # BC Math Binary
  16. IDBIN="" # Identify Binary
  17. PDFINFOBIN="" # PDF Info Binary
  18. MDLSBIN="" # MacOS mdls Binary
  19. ##################### ENVIRONMENT SET-UP #######################
  20. LC_MEASUREMENT="C" # To make sure our numbers have .decimals
  21. LC_ALL="C" # Some languages use , as decimal token
  22. LC_CTYPE="C"
  23. LC_NUMERIC="C"
  24. TRUE=0 # Silly stuff
  25. FALSE=1
  26. ########################### GLOBALS ############################
  27. SCALE="0.95" # scaling factor (0.95 = 95%, e.g.)
  28. VERBOSE=0 # verbosity Level
  29. PDFSCALE_NAME="$(basename $0)" # simplified name of this script
  30. OSNAME="$(uname 2>/dev/null)" # Check where we are running
  31. GS_RUN_STATUS="" # Holds GS error messages, signals errors
  32. INFILEPDF="" # Input PDF file name
  33. OUTFILEPDF="" # Output PDF file name
  34. JUST_IDENTIFY=$FALSE # Flag to just show PDF info
  35. ABORT_ON_OVERWRITE=$FALSE # Flag to abort if OUTFILEPDF already exists
  36. ADAPTIVEMODE=$TRUE # Automatically try to guess best mode
  37. AUTOMATIC_SCALING=$TRUE # Default scaling in $SCALE, disabled in resize mode
  38. MODE="" # Which page size detection to use
  39. RESIZE_PAPER_TYPE="" # Pre-defined paper to use
  40. CUSTOM_RESIZE_PAPER=$FALSE # If we are using a custom-defined paper
  41. FLIP_DETECTION=$TRUE # If we should run the Flip-detection
  42. FLIP_FORCE=$FALSE # If we should force Flipping
  43. AUTO_ROTATION='/PageByPage' # GS call auto-rotation setting
  44. PGWIDTH="" # Input PDF Page Width
  45. PGHEIGHT="" # Input PDF Page Height
  46. RESIZE_WIDTH="" # Resized PDF Page Width
  47. RESIZE_HEIGHT="" # Resized PDF Page Height
  48. ########################## EXIT FLAGS ##########################
  49. EXIT_SUCCESS=0
  50. EXIT_ERROR=1
  51. EXIT_INVALID_PAGE_SIZE_DETECTED=10
  52. EXIT_FILE_NOT_FOUND=20
  53. EXIT_INPUT_NOT_PDF=21
  54. EXIT_INVALID_OPTION=22
  55. EXIT_NO_INPUT_FILE=23
  56. EXIT_INVALID_SCALE=24
  57. EXIT_MISSING_DEPENDENCY=25
  58. EXIT_IMAGEMAGIK_NOT_FOUND=26
  59. EXIT_MAC_MDLS_NOT_FOUND=27
  60. EXIT_PDFINFO_NOT_FOUND=28
  61. EXIT_NOWRITE_PERMISSION=29
  62. EXIT_NOREAD_PERMISSION=30
  63. EXIT_TEMP_FILE_EXISTS=40
  64. EXIT_INVALID_PAPER_SIZE=50
  65. ############################# MAIN #############################
  66. # Main function called at the end
  67. main() {
  68. printPDFSizes # may exit here
  69. local finalRet=$EXIT_ERROR
  70. if isMixedMode; then
  71. initMain " Mixed Tasks: Resize & Scale"
  72. local tempFile=""
  73. local tempSuffix="$RANDOM$RANDOM""_TEMP_$RANDOM$RANDOM.pdf"
  74. outputFile="$OUTFILEPDF" # backup outFile name
  75. tempFile="${OUTFILEPDF%.pdf}.$tempSuffix" # set a temp file name
  76. if isFile "$tempFile"; then
  77. printError $'Error! Temporary file name already exists!\n'"File: $tempFile"$'\nAborting execution to avoid overwriting the file.\nPlease Try again...'
  78. exit $EXIT_TEMP_FILE_EXISTS
  79. fi
  80. OUTFILEPDF="$tempFile" # set output to tmp file
  81. pageResize # resize to tmp file
  82. finalRet=$?
  83. INFILEPDF="$tempFile" # get tmp file as input
  84. OUTFILEPDF="$outputFile" # reset final target
  85. PGWIDTH=$RESIZE_WIDTH # we already know the new page size
  86. PGHEIGHT=$RESIZE_HEIGHT # from the last command (Resize)
  87. vPrintPageSizes ' New'
  88. vPrintScaleFactor
  89. pageScale # scale the resized pdf
  90. finalRet=$(($finalRet+$?))
  91. # remove tmp file
  92. isFile "$tempFile" && rm "$tempFile" >/dev/null 2>&1 || printError "Error when removing temporary file: $tempFile"
  93. elif isResizeMode; then
  94. initMain " Single Task: Resize PDF Paper"
  95. vPrintScaleFactor "Disabled (resize only)"
  96. pageResize
  97. finalRet=$?
  98. else
  99. initMain " Single Task: Scale PDF Contents"
  100. local scaleMode=""
  101. isManualScaledMode && scaleMode='(manual)' || scaleMode='(auto)'
  102. vPrintScaleFactor "$SCALE $scaleMode"
  103. pageScale
  104. finalRet=$?
  105. fi
  106. if [[ $finalRet -eq $EXIT_SUCCESS ]] && isEmpty "$GS_RUN_STATUS"; then
  107. vprint " Final Status: File created successfully"
  108. else
  109. vprint " Final Status: Error detected. Exit status: $finalRet"
  110. printError "PdfScale: ERROR!"$'\n'"Ghostscript Debug Info:"$'\n'"$GS_RUN_STATUS"
  111. fi
  112. return $finalRet
  113. }
  114. # Initializes PDF processing for all modes of operation
  115. initMain() {
  116. printVersion 1 'verbose'
  117. isNotEmpty "$1" && vprint "$1"
  118. vPrintFileInfo
  119. getPageSize
  120. vPrintPageSizes ' Source'
  121. }
  122. # Prints PDF Info and exits with $EXIT_SUCCESS, but only if $JUST_IDENTIFY is $TRUE
  123. printPDFSizes() {
  124. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  125. VERBOSE=0
  126. printVersion 3 " - Paper Sizes"
  127. getPageSize || initError "Could not get pagesize!"
  128. local paperType="$(getGSPaperName $PGWIDTH $PGHEIGHT)"
  129. isEmpty "$paperType" && paperType="Custom Paper Size"
  130. printf '%s\n' "------------+-----------------------------"
  131. printf " File | %s\n" "$(basename "$INFILEPDF")"
  132. printf " Paper Type | %s\n" "$paperType"
  133. printf '%s\n' "------------+-----------------------------"
  134. printf '%s\n' " | WIDTH x HEIGHT"
  135. printf " Points | %+8s x %-8s\n" "$PGWIDTH" "$PGHEIGHT"
  136. printf " Milimeters | %+8s x %-8s\n" "$(pointsToMilimeters $PGWIDTH)" "$(pointsToMilimeters $PGHEIGHT)"
  137. printf " Inches | %+8s x %-8s\n" "$(pointsToInches $PGWIDTH)" "$(pointsToInches $PGHEIGHT)"
  138. exit $EXIT_SUCCESS
  139. fi
  140. return $EXIT_SUCCESS
  141. }
  142. ###################### GHOSTSCRIPT CALLS #######################
  143. # Runs the ghostscript scaling script
  144. pageScale() {
  145. # Compute translation factors (to center page).
  146. XTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN")
  147. YTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN")
  148. vprint " Translation X: $XTRANS"
  149. vprint " Translation Y: $YTRANS"
  150. local increase=$(echo "scale=0; (($SCALE - 1) * 100)/1" | "$BCBIN")
  151. vprint " Run Scaling: $increase %"
  152. GS_RUN_STATUS="$GS_RUN_STATUS""$(gsPageScale 2>&1)"
  153. return $? # Last command is always returned I think
  154. }
  155. # Runs GS call for scaling, nothing else should run here
  156. gsPageScale() {
  157. # Scale page
  158. "$GSBIN" \
  159. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  160. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  161. -dColorConversionStrategy=/LeaveColorUnchanged \
  162. -dSubsetFonts=true -dEmbedAllFonts=true \
  163. -dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \
  164. -sOutputFile="$OUTFILEPDF" \
  165. -c "<</BeginPage{$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
  166. -f "$INFILEPDF"
  167. return $?
  168. }
  169. # Runs the ghostscript paper resize script
  170. pageResize() {
  171. # Get paper sizes from source if not resizing
  172. isResizePaperSource && { RESIZE_WIDTH=$PGWIDTH; RESIZE_HEIGHT=$PGHEIGHT; }
  173. # Get new paper sizes if not custom or source paper
  174. isNotCustomPaper && ! isResizePaperSource && getGSPaperSize "$RESIZE_PAPER_TYPE"
  175. vprint " Auto Rotate: $(basename $AUTO_ROTATION)"
  176. runFlipDetect
  177. vprint " Run Resizing: $(uppercase "$RESIZE_PAPER_TYPE") ( "$RESIZE_WIDTH" x "$RESIZE_HEIGHT" ) pts"
  178. GS_RUN_STATUS="$GS_RUN_STATUS""$(gsPageResize 2>&1)"
  179. return $?
  180. }
  181. # Runs GS call for resizing, nothing else should run here
  182. gsPageResize() {
  183. # Change page size
  184. "$GSBIN" \
  185. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  186. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  187. -dColorConversionStrategy=/LeaveColorUnchanged \
  188. -dSubsetFonts=true -dEmbedAllFonts=true \
  189. -dDEVICEWIDTHPOINTS=$RESIZE_WIDTH -dDEVICEHEIGHTPOINTS=$RESIZE_HEIGHT \
  190. -dAutoRotatePages=$AUTO_ROTATION \
  191. -dFIXEDMEDIA -dPDFFitPage \
  192. -sOutputFile="$OUTFILEPDF" \
  193. -f "$INFILEPDF"
  194. return $?
  195. }
  196. # Returns $TRUE if we should use the source paper size, $FALSE otherwise
  197. isResizePaperSource() {
  198. [[ "$RESIZE_PAPER_TYPE" = 'source' ]] && return $TRUE
  199. return $FALSE
  200. }
  201. # Filp-Detect Logic
  202. runFlipDetect() {
  203. if isFlipForced; then
  204. vprint " Flip Detect: Forced Mode!"
  205. applyFlipRevert
  206. elif isFlipDetectionEnabled && shouldFlip; then
  207. vprint " Flip Detect: Wrong orientation detected!"
  208. applyFlipRevert
  209. elif ! isFlipDetectionEnabled; then
  210. vprint " Flip Detect: Disabled"
  211. else
  212. vprint " Flip Detect: No change needed"
  213. fi
  214. }
  215. # Inverts $RESIZE_HEIGHT with $RESIZE_WIDTH
  216. applyFlipRevert() {
  217. local tmpInverter=""
  218. tmpInverter=$RESIZE_HEIGHT
  219. RESIZE_HEIGHT=$RESIZE_WIDTH
  220. RESIZE_WIDTH=$tmpInverter
  221. vprint " Inverting Width <-> Height"
  222. }
  223. # Returns the $FLIP_DETECTION flag
  224. isFlipDetectionEnabled() {
  225. return $FLIP_DETECTION
  226. }
  227. # Returns the $FLIP_FORCE flag
  228. isFlipForced() {
  229. return $FLIP_FORCE
  230. }
  231. # Returns $TRUE if the the paper size will invert orientation from source, $FALSE otherwise
  232. shouldFlip() {
  233. [[ $PGWIDTH -gt $PGHEIGHT && $RESIZE_WIDTH -lt $RESIZE_HEIGHT ]] || [[ $PGWIDTH -lt $PGHEIGHT && $RESIZE_WIDTH -gt $RESIZE_HEIGHT ]] && return $TRUE
  234. return $FALSE
  235. }
  236. ########################## INITIALIZERS #########################
  237. # Loads external dependencies and checks for errors
  238. initDeps() {
  239. GREPBIN="$(which grep 2>/dev/null)"
  240. GSBIN="$(which gs 2>/dev/null)"
  241. BCBIN="$(which bc 2>/dev/null)"
  242. IDBIN=$(which identify 2>/dev/null)
  243. MDLSBIN="$(which mdls 2>/dev/null)"
  244. PDFINFOBIN="$(which pdfinfo 2>/dev/null)"
  245. vprint "Checking for basename, grep, ghostscript and bcmath"
  246. basename "" >/dev/null 2>&1 || printDependency 'basename'
  247. isNotAvailable "$GREPBIN" && printDependency 'grep'
  248. isNotAvailable "$GSBIN" && printDependency 'ghostscript'
  249. isNotAvailable "$BCBIN" && printDependency 'bc'
  250. return $TRUE
  251. }
  252. # Checks for dependencies errors, run after getting options
  253. checkDeps() {
  254. if [[ $MODE = "IDENTIFY" ]]; then
  255. vprint "Checking for imagemagick's identify"
  256. if isNotAvailable "$IDBIN"; then printDependency 'imagemagick'; fi
  257. fi
  258. if [[ $MODE = "PDFINFO" ]]; then
  259. vprint "Checking for pdfinfo"
  260. if isNotAvailable "$PDFINFOBIN"; then printDependency 'pdfinfo'; fi
  261. fi
  262. if [[ $MODE = "MDLS" ]]; then
  263. vprint "Checking for MacOS mdls"
  264. if isNotAvailable "$MDLSBIN"; then
  265. initError 'mdls executable was not found! Is this even MacOS?' $EXIT_MAC_MDLS_NOT_FOUND
  266. fi
  267. fi
  268. return $TRUE
  269. }
  270. ######################### CLI OPTIONS ##########################
  271. # Parse options
  272. getOptions() {
  273. local _optArgs=() # things that do not start with a '-'
  274. local _tgtFile="" # to set $OUTFILEPDF
  275. local _currParam="" # to enable case-insensitiveness
  276. while [ ${#} -gt 0 ]; do
  277. if [[ "${1:0:2}" = '--' ]]; then
  278. # Long Option, get lowercase version
  279. _currParam="$(lowercase ${1})"
  280. elif [[ "${1:0:1}" = '-' ]]; then
  281. # short Option, just assign
  282. _currParam="${1}"
  283. else
  284. # file name arguments, store as is and reset loop
  285. _optArgs+=("$1")
  286. shift
  287. continue
  288. fi
  289. case "$_currParam" in
  290. -v|--verbose)
  291. ((VERBOSE++))
  292. shift
  293. ;;
  294. -n|--no-overwrite|--nooverwrite)
  295. ABORT_ON_OVERWRITE=$TRUE
  296. shift
  297. ;;
  298. -h|--help)
  299. printHelp
  300. exit $EXIT_SUCCESS
  301. ;;
  302. -V|--version)
  303. printVersion 3
  304. exit $EXIT_SUCCESS
  305. ;;
  306. -i|--identify|--info)
  307. JUST_IDENTIFY=$TRUE
  308. shift
  309. ;;
  310. -s|--scale|--setscale|--set-scale)
  311. shift
  312. parseScale "$1"
  313. shift
  314. ;;
  315. -m|--mode|--paperdetect|--paper-detect|--pagesizemode|--page-size-mode)
  316. shift
  317. parseMode "$1"
  318. shift
  319. ;;
  320. -r|--resize)
  321. shift
  322. parsePaperResize "$1"
  323. shift
  324. ;;
  325. -p|--printpapers|--print-papers|--listpapers|--list-papers)
  326. printPaperInfo
  327. exit $EXIT_SUCCESS
  328. ;;
  329. -f|--flipdetection|--flip-detection|--flip-mode|--flipmode|--flipdetect|--flip-detect)
  330. shift
  331. parseFlipDetectionMode "$1"
  332. shift
  333. ;;
  334. -a|--autorotation|--auto-rotation|--autorotate|--auto-rotate)
  335. shift
  336. parseAutoRotationMode "$1"
  337. shift
  338. ;;
  339. *)
  340. initError "Invalid Parameter: \"$1\"" $EXIT_INVALID_OPTION
  341. ;;
  342. esac
  343. done
  344. isEmpty "${_optArgs[2]}" || initError "Seems like you passed an extra file name?"$'\n'"Invalid option: ${_optArgs[2]}" $EXIT_INVALID_OPTION
  345. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  346. isEmpty "${_optArgs[1]}" || initError "Seems like you passed an extra file name?"$'\n'"Invalid option: ${_optArgs[1]}" $EXIT_INVALID_OPTION
  347. VERBOSE=0 # remove verboseness if present
  348. fi
  349. # Validate input PDF file
  350. INFILEPDF="${_optArgs[0]}"
  351. isEmpty "$INFILEPDF" && initError "Input file is empty!" $EXIT_NO_INPUT_FILE
  352. isPDF "$INFILEPDF" || initError "Input file is not a PDF file: $INFILEPDF" $EXIT_INPUT_NOT_PDF
  353. isFile "$INFILEPDF" || initError "Input file not found: $INFILEPDF" $EXIT_FILE_NOT_FOUND
  354. isReadable "$INFILEPDF" || initError "No read access to input file: $INFILEPDF"$'\nPermission Denied' $EXIT_NOREAD_PERMISSION
  355. checkDeps
  356. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  357. return $TRUE # no need to get output file, so return already
  358. fi
  359. _tgtFile="${_optArgs[1]}"
  360. local _autoName="${INFILEPDF%.*}" # remove possible stupid extension, like .pDF
  361. if isMixedMode; then
  362. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.$(uppercase $RESIZE_PAPER_TYPE).SCALED.pdf"
  363. elif isResizeMode; then
  364. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.$(uppercase $RESIZE_PAPER_TYPE).pdf"
  365. else
  366. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.SCALED.pdf"
  367. fi
  368. isNotEmpty "$_tgtFile" && OUTFILEPDF="${_tgtFile%.pdf}.pdf"
  369. validateOutFile
  370. }
  371. # Checks if output file is valid and writable
  372. validateOutFile() {
  373. local _tgtDir="$(dirname "$OUTFILEPDF")"
  374. isDir "$_tgtDir" || initError "Output directory does not exist!"$'\n'"Target Dir: $_tgtDir" $EXIT_NOWRITE_PERMISSION
  375. isAbortOnOverwrite && isFile "$OUTFILEPDF" && initError $'Output file already exists and --no-overwrite was used!\nRemove the "-n" or "--no-overwrite" option if you want to overwrite the file\n'"Target File: $OUTFILEPDF" $EXIT_NOWRITE_PERMISSION
  376. isTouchable "$OUTFILEPDF" || initError "Could not get write permission for output file!"$'\n'"Target File: $OUTFILEPDF"$'\nPermission Denied' $EXIT_NOWRITE_PERMISSION
  377. }
  378. # Returns $TRUE if we should not overwrite $OUTFILEPDF, $FALSE otherwise
  379. isAbortOnOverwrite() {
  380. return $ABORT_ON_OVERWRITE
  381. }
  382. # Parses and validates the scaling factor
  383. parseScale() {
  384. AUTOMATIC_SCALING=$FALSE
  385. if ! isFloatBiggerThanZero "$1"; then
  386. printError "Invalid factor: $1"
  387. printError "The factor must be a floating point number greater than 0"
  388. printError "Example: for 80% use 0.8"
  389. exit $EXIT_INVALID_SCALE
  390. fi
  391. SCALE="$1"
  392. }
  393. # Parse a forced mode of operation
  394. parseMode() {
  395. local param="$(lowercase $1)"
  396. case "${param}" in
  397. c|catgrep|'cat+grep'|grep|g)
  398. ADAPTIVEMODE=$FALSE
  399. MODE="CATGREP"
  400. return $TRUE
  401. ;;
  402. i|imagemagick|identify)
  403. ADAPTIVEMODE=$FALSE
  404. MODE="IDENTIFY"
  405. return $TRUE
  406. ;;
  407. m|mdls|quartz|mac)
  408. ADAPTIVEMODE=$FALSE
  409. MODE="MDLS"
  410. return $TRUE
  411. ;;
  412. p|pdfinfo)
  413. ADAPTIVEMODE=$FALSE
  414. MODE="PDFINFO"
  415. return $TRUE
  416. ;;
  417. a|auto|automatic|adaptive)
  418. ADAPTIVEMODE=$TRUE
  419. MODE=""
  420. return $TRUE
  421. ;;
  422. *)
  423. initError "Invalid PDF Size Detection Mode: \"$1\"" $EXIT_INVALID_OPTION
  424. return $FALSE
  425. ;;
  426. esac
  427. return $FALSE
  428. }
  429. # Parses and validates the scaling factor
  430. parseFlipDetectionMode() {
  431. local param="$(lowercase $1)"
  432. case "${param}" in
  433. d|disable)
  434. FLIP_DETECTION=$FALSE
  435. FLIP_FORCE=$FALSE
  436. ;;
  437. f|force)
  438. FLIP_DETECTION=$FALSE
  439. FLIP_FORCE=$TRUE
  440. ;;
  441. a|auto|automatic)
  442. FLIP_DETECTION=$TRUE
  443. FLIP_FORCE=$FALSE
  444. ;;
  445. *)
  446. initError "Invalid Flip Detection Mode: \"$1\"" $EXIT_INVALID_OPTION
  447. return $FALSE
  448. ;;
  449. esac
  450. }
  451. # Parses and validates the scaling factor
  452. parseAutoRotationMode() {
  453. local param="$(lowercase $1)"
  454. case "${param}" in
  455. n|none)
  456. AUTO_ROTATION='/None'
  457. ;;
  458. a|all)
  459. AUTO_ROTATION='/All'
  460. ;;
  461. p|pagebypage|auto)
  462. AUTO_ROTATION='/PageByPage'
  463. ;;
  464. *)
  465. initError "Invalid Auto Rotation Mode: \"$1\"" $EXIT_INVALID_OPTION
  466. return $FALSE
  467. ;;
  468. esac
  469. }
  470. # Validades the a paper resize CLI option and sets the paper to $RESIZE_PAPER_TYPE
  471. parsePaperResize() {
  472. isEmpty "$1" && initError 'Invalid Paper Type: (empty)' $EXIT_INVALID_PAPER_SIZE
  473. local lowercasePaper="$(lowercase $1)"
  474. local customPaper=($lowercasePaper)
  475. if [[ "$customPaper" = 'same' || "$customPaper" = 'keep' || "$customPaper" = 'source' ]]; then
  476. RESIZE_PAPER_TYPE='source'
  477. elif [[ "${customPaper[0]}" = 'custom' ]]; then
  478. if isNotValidMeasure "${customPaper[1]}" || ! isFloatBiggerThanZero "${customPaper[2]}" || ! isFloatBiggerThanZero "${customPaper[3]}"; then
  479. initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom <measurement> <width> <height>'"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION
  480. fi
  481. RESIZE_PAPER_TYPE="custom"
  482. CUSTOM_RESIZE_PAPER=$TRUE
  483. if isMilimeter "${customPaper[1]}"; then
  484. RESIZE_WIDTH="$(milimetersToPoints "${customPaper[2]}")"
  485. RESIZE_HEIGHT="$(milimetersToPoints "${customPaper[3]}")"
  486. elif isInch "${customPaper[1]}"; then
  487. RESIZE_WIDTH="$(inchesToPoints "${customPaper[2]}")"
  488. RESIZE_HEIGHT="$(inchesToPoints "${customPaper[3]}")"
  489. elif isPoint "${customPaper[1]}"; then
  490. RESIZE_WIDTH="${customPaper[2]}"
  491. RESIZE_HEIGHT="${customPaper[3]}"
  492. else
  493. initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom <measurement> <width> <height>'"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION
  494. fi
  495. else
  496. isPaperName "$lowercasePaper" || initError "Invalid Paper Type: $1" $EXIT_INVALID_PAPER_SIZE
  497. RESIZE_PAPER_TYPE="$lowercasePaper"
  498. fi
  499. }
  500. ################### PDF PAGE SIZE DETECTION ####################
  501. ################################################################
  502. # Detects operation mode and also runs the adaptive mode
  503. # PAGESIZE LOGIC
  504. # 1- Try to get Mediabox with GREP
  505. # 2- MacOS => try to use mdls
  506. # 3- Try to use pdfinfo
  507. # 4- Try to use identify (imagemagick)
  508. # 5- Fail
  509. ################################################################
  510. getPageSize() {
  511. if isNotAdaptiveMode; then
  512. vprint " Get Page Size: Adaptive Disabled"
  513. if [[ $MODE = "CATGREP" ]]; then
  514. vprint " Method: Grep"
  515. getPageSizeCatGrep
  516. elif [[ $MODE = "MDLS" ]]; then
  517. vprint " Method: Mac Quartz mdls"
  518. getPageSizeMdls
  519. elif [[ $MODE = "PDFINFO" ]]; then
  520. vprint " Method: PDFInfo"
  521. getPageSizePdfInfo
  522. elif [[ $MODE = "IDENTIFY" ]]; then
  523. vprint " Method: ImageMagick's Identify"
  524. getPageSizeImagemagick
  525. else
  526. printError "Error! Invalid Mode: $MODE"
  527. printError "Aborting execution..."
  528. exit $EXIT_INVALID_OPTION
  529. fi
  530. return $TRUE
  531. fi
  532. vprint " Get Page Size: Adaptive Enabled"
  533. vprint " Method: Grep"
  534. getPageSizeCatGrep
  535. if pageSizeIsInvalid && [[ $OSNAME = "Darwin" ]]; then
  536. vprint " Failed"
  537. vprint " Method: Mac Quartz mdls"
  538. getPageSizeMdls
  539. fi
  540. if pageSizeIsInvalid; then
  541. vprint " Failed"
  542. vprint " Method: PDFInfo"
  543. getPageSizePdfInfo
  544. fi
  545. if pageSizeIsInvalid; then
  546. vprint " Failed"
  547. vprint " Method: ImageMagick's Identify"
  548. getPageSizeImagemagick
  549. fi
  550. if pageSizeIsInvalid; then
  551. vprint " Failed"
  552. printError "Error when detecting PDF paper size!"
  553. printError "All methods of detection failed"
  554. printError "You may want to install pdfinfo or imagemagick"
  555. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  556. fi
  557. return $TRUE
  558. }
  559. # Gets page size using imagemagick's identify
  560. getPageSizeImagemagick() {
  561. # Sanity and Adaptive together
  562. if isNotFile "$IDBIN" && isNotAdaptiveMode; then
  563. notAdaptiveFailed "Make sure you installed ImageMagick and have identify on your \$PATH" "ImageMagick's Identify"
  564. elif isNotFile "$IDBIN" && isAdaptiveMode; then
  565. return $FALSE
  566. fi
  567. # get data from image magick
  568. local identify="$("$IDBIN" -format '%[fx:w] %[fx:h]BREAKME' "$INFILEPDF" 2>/dev/null)"
  569. if isEmpty "$identify" && isNotAdaptiveMode; then
  570. notAdaptiveFailed "ImageMagicks's Identify returned an empty string!"
  571. elif isEmpty "$identify" && isAdaptiveMode; then
  572. return $FALSE
  573. fi
  574. identify="${identify%%BREAKME*}" # get page size only for 1st page
  575. identify=($identify) # make it an array
  576. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  577. PGHEIGHT=$(printf '%.0f' "${identify[1]}") # assign
  578. return $TRUE
  579. }
  580. # Gets page size using Mac Quarts mdls
  581. getPageSizeMdls() {
  582. # Sanity and Adaptive together
  583. if isNotFile "$MDLSBIN" && isNotAdaptiveMode; then
  584. notAdaptiveFailed "Are you even trying this on a Mac?" "Mac Quartz mdls"
  585. elif isNotFile "$MDLSBIN" && isAdaptiveMode; then
  586. return $FALSE
  587. fi
  588. local identify="$("$MDLSBIN" -mdls -name kMDItemPageHeight -name kMDItemPageWidth "$INFILEPDF" 2>/dev/null)"
  589. if isEmpty "$identify" && isNotAdaptiveMode; then
  590. notAdaptiveFailed "Mac Quartz mdls returned an empty string!"
  591. elif isEmpty "$identify" && isAdaptiveMode; then
  592. return $FALSE
  593. fi
  594. identify=${identify//$'\t'/ } # change tab to space
  595. identify=($identify) # make it an array
  596. if [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isNotAdaptiveMode; then
  597. notAdaptiveFailed "There was no metadata to read from the file! Is Spotlight OFF?"
  598. elif [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isAdaptiveMode; then
  599. return $FALSE
  600. fi
  601. PGWIDTH=$(printf '%.0f' "${identify[5]}") # assign
  602. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  603. return $TRUE
  604. }
  605. # Gets page size using Linux PdfInfo
  606. getPageSizePdfInfo() {
  607. # Sanity and Adaptive together
  608. if isNotFile "$PDFINFOBIN" && isNotAdaptiveMode; then
  609. notAdaptiveFailed "Do you have pdfinfo installed and available on your \$PATH?" "Linux pdfinfo"
  610. elif isNotFile "$PDFINFOBIN" && isAdaptiveMode; then
  611. return $FALSE
  612. fi
  613. # get data from image magick
  614. local identify="$("$PDFINFOBIN" "$INFILEPDF" 2>/dev/null | "$GREPBIN" -i 'Page size:' )"
  615. if isEmpty "$identify" && isNotAdaptiveMode; then
  616. notAdaptiveFailed "Linux PdfInfo returned an empty string!"
  617. elif isEmpty "$identify" && isAdaptiveMode; then
  618. return $FALSE
  619. fi
  620. identify="${identify##*Page size:}" # remove stuff
  621. identify=($identify) # make it an array
  622. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  623. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  624. return $TRUE
  625. }
  626. # Gets page size using cat and grep
  627. getPageSizeCatGrep() {
  628. # get MediaBox info from PDF file using grep, these are all possible
  629. # /MediaBox [0 0 595 841]
  630. # /MediaBox [ 0 0 595.28 841.89]
  631. # /MediaBox[ 0 0 595.28 841.89 ]
  632. # Get MediaBox data if possible
  633. local mediaBox="$("$GREPBIN" -a -e '/MediaBox' -m 1 "$INFILEPDF" 2>/dev/null)"
  634. mediaBox="${mediaBox##*/MediaBox}"
  635. # No page size data available
  636. if isEmpty "$mediaBox" && isNotAdaptiveMode; then
  637. notAdaptiveFailed "There is no MediaBox in the pdf document!"
  638. elif isEmpty "$mediaBox" && isAdaptiveMode; then
  639. return $FALSE
  640. fi
  641. # remove chars [ and ]
  642. mediaBox="${mediaBox//[}"
  643. mediaBox="${mediaBox//]}"
  644. mediaBox=($mediaBox) # make it an array
  645. mbCount=${#mediaBox[@]} # array size
  646. # sanity
  647. if [[ $mbCount -lt 4 ]]; then
  648. printError "Error when reading the page size!"
  649. printError "The page size information is invalid!"
  650. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  651. fi
  652. # we are done
  653. PGWIDTH=$(printf '%.0f' "${mediaBox[2]}") # Get Round Width
  654. PGHEIGHT=$(printf '%.0f' "${mediaBox[3]}") # Get Round Height
  655. return $TRUE
  656. }
  657. # Prints error message and exits execution
  658. notAdaptiveFailed() {
  659. local errProgram="$2"
  660. local errStr="$1"
  661. if isEmpty "$2"; then
  662. printError "Error when reading input file!"
  663. printError "Could not determine the page size!"
  664. else
  665. printError "Error! $2 was not found!"
  666. fi
  667. isNotEmpty "$errStr" && printError "$errStr"
  668. printError "Aborting! You may want to try the adaptive mode."
  669. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  670. }
  671. # Verbose print of the Width and Height (Source or New) to screen
  672. vPrintPageSizes() {
  673. vprint " $1 Width: $PGWIDTH postscript-points"
  674. vprint "$1 Height: $PGHEIGHT postscript-points"
  675. }
  676. #################### GHOSTSCRIPT PAPER INFO ####################
  677. # Loads valid paper info to memory
  678. getPaperInfo() {
  679. # name inchesW inchesH mmW mmH pointsW pointsH
  680. sizesUS="\
  681. 11x17 11.0 17.0 279 432 792 1224
  682. ledger 17.0 11.0 432 279 1224 792
  683. legal 8.5 14.0 216 356 612 1008
  684. letter 8.5 11.0 216 279 612 792
  685. lettersmall 8.5 11.0 216 279 612 792
  686. archE 36.0 48.0 914 1219 2592 3456
  687. archD 24.0 36.0 610 914 1728 2592
  688. archC 18.0 24.0 457 610 1296 1728
  689. archB 12.0 18.0 305 457 864 1296
  690. archA 9.0 12.0 229 305 648 864"
  691. sizesISO="\
  692. a0 33.1 46.8 841 1189 2384 3370
  693. a1 23.4 33.1 594 841 1684 2384
  694. a2 16.5 23.4 420 594 1191 1684
  695. a3 11.7 16.5 297 420 842 1191
  696. a4 8.3 11.7 210 297 595 842
  697. a4small 8.3 11.7 210 297 595 842
  698. a5 5.8 8.3 148 210 420 595
  699. a6 4.1 5.8 105 148 297 420
  700. a7 2.9 4.1 74 105 210 297
  701. a8 2.1 2.9 52 74 148 210
  702. a9 1.5 2.1 37 52 105 148
  703. a10 1.0 1.5 26 37 73 105
  704. isob0 39.4 55.7 1000 1414 2835 4008
  705. isob1 27.8 39.4 707 1000 2004 2835
  706. isob2 19.7 27.8 500 707 1417 2004
  707. isob3 13.9 19.7 353 500 1001 1417
  708. isob4 9.8 13.9 250 353 709 1001
  709. isob5 6.9 9.8 176 250 499 709
  710. isob6 4.9 6.9 125 176 354 499
  711. c0 36.1 51.1 917 1297 2599 3677
  712. c1 25.5 36.1 648 917 1837 2599
  713. c2 18.0 25.5 458 648 1298 1837
  714. c3 12.8 18.0 324 458 918 1298
  715. c4 9.0 12.8 229 324 649 918
  716. c5 6.4 9.0 162 229 459 649
  717. c6 4.5 6.4 114 162 323 459"
  718. sizesJIS="\
  719. jisb0 NA NA 1030 1456 2920 4127
  720. jisb1 NA NA 728 1030 2064 2920
  721. jisb2 NA NA 515 728 1460 2064
  722. jisb3 NA NA 364 515 1032 1460
  723. jisb4 NA NA 257 364 729 1032
  724. jisb5 NA NA 182 257 516 729
  725. jisb6 NA NA 128 182 363 516"
  726. sizesOther="\
  727. flsa 8.5 13.0 216 330 612 936
  728. flse 8.5 13.0 216 330 612 936
  729. halfletter 5.5 8.5 140 216 396 612
  730. hagaki 3.9 5.8 100 148 283 420"
  731. sizesAll="\
  732. $sizesUS
  733. $sizesISO
  734. $sizesJIS
  735. $sizesOther"
  736. }
  737. # Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT
  738. getGSPaperSize() {
  739. isEmpty "$sizesall" && getPaperInfo
  740. while read l; do
  741. local cols=($l)
  742. if [[ "$1" == ${cols[0]} ]]; then
  743. RESIZE_WIDTH=${cols[5]}
  744. RESIZE_HEIGHT=${cols[6]}
  745. return $TRUE
  746. fi
  747. done <<< "$sizesAll"
  748. }
  749. # Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT
  750. getGSPaperName() {
  751. local w="$(printf "%.0f" $1)"
  752. local h="$(printf "%.0f" $2)"
  753. isEmpty "$sizesall" && getPaperInfo
  754. # Because US Standard has inverted sizes, I need to scan 2 times
  755. # instead of just testing if width is bigger than height
  756. while read l; do
  757. local cols=($l)
  758. if [[ "$w" == ${cols[5]} && "$h" == ${cols[6]} ]]; then
  759. printf "%s Portrait" $(uppercase ${cols[0]})
  760. return $TRUE
  761. fi
  762. done <<< "$sizesAll"
  763. while read l; do
  764. local cols=($l)
  765. if [[ "$w" == ${cols[6]} && "$h" == ${cols[5]} ]]; then
  766. printf "%s Landscape" $(uppercase ${cols[0]})
  767. return $TRUE
  768. fi
  769. done <<< "$sizesAll"
  770. return $FALSE
  771. }
  772. # Loads an array with paper names to memory
  773. getPaperNames() {
  774. 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 \
  775. 11x17 ledger legal letter lettersmall archE archD archC archB archA \
  776. jisb0 jisb1 jisb2 jisb3 jisb4 jisb5 jisb6 \
  777. flsa flse halfletter hagaki)
  778. }
  779. # Prints uppercase paper names to screen (used in help)
  780. printPaperNames() {
  781. isEmpty "$paperNames" && getPaperNames
  782. for i in "${!paperNames[@]}"; do
  783. [[ $i -eq 0 ]] && echo -n -e ' '
  784. [[ $i -ne 0 && $((i % 5)) -eq 0 ]] && echo -n -e $'\n '
  785. ppN="$(uppercase ${paperNames[i]})"
  786. printf "%-14s" "$ppN"
  787. done
  788. echo ""
  789. }
  790. # Returns $TRUE if $! is a valid paper name, $FALSE otherwise
  791. isPaperName() {
  792. isEmpty "$1" && return $FALSE
  793. isEmpty "$paperNames" && getPaperNames
  794. for i in "${paperNames[@]}"; do
  795. [[ "$i" = "$1" ]] && return $TRUE
  796. done
  797. return $FALSE
  798. }
  799. # Prints all tables with ghostscript paper information
  800. printPaperInfo() {
  801. printVersion 3
  802. echo $'\n'"Paper Sizes Information"$'\n'
  803. getPaperInfo
  804. printPaperTable "ISO STANDARD" "$sizesISO"; echo
  805. printPaperTable "US STANDARD" "$sizesUS"; echo
  806. printPaperTable "JIS STANDARD *Aproximated Points" "$sizesJIS"; echo
  807. printPaperTable "OTHERS" "$sizesOther"; echo
  808. }
  809. # GS paper table helper, prints a full line
  810. printTableLine() {
  811. echo '+-----------------------------------------------------------------+'
  812. }
  813. # GS paper table helper, prints a line with dividers
  814. printTableDivider() {
  815. echo '+-----------------+-------+-------+-------+-------+-------+-------+'
  816. }
  817. # GS paper table helper, prints a table header
  818. printTableHeader() {
  819. echo '| Name | inchW | inchH | mm W | mm H | pts W | pts H |'
  820. }
  821. # GS paper table helper, prints a table title
  822. printTableTitle() {
  823. printf "| %-64s%s\n" "$1" '|'
  824. }
  825. # GS paper table printer, prints a table for a paper variable
  826. printPaperTable() {
  827. printTableLine
  828. printTableTitle "$1"
  829. printTableLine
  830. printTableHeader
  831. printTableDivider
  832. while read l; do
  833. local cols=($l)
  834. printf "| %-15s | %+5s | %+5s | %+5s | %+5s | %+5s | %+5s |\n" ${cols[*]};
  835. done <<< "$2"
  836. printTableDivider
  837. }
  838. # Returns $TRUE if $1 is a valid measurement for a custom paper, $FALSE otherwise
  839. isNotValidMeasure() {
  840. isMilimeter "$1" || isInch "$1" || isPoint "$1" && return $FALSE
  841. return $TRUE
  842. }
  843. # Returns $TRUE if $1 is a valid milimeter string, $FALSE otherwise
  844. isMilimeter() {
  845. [[ "$1" = 'mm' || "$1" = 'milimeters' || "$1" = 'milimeter' ]] && return $TRUE
  846. return $FALSE
  847. }
  848. # Returns $TRUE if $1 is a valid inch string, $FALSE otherwise
  849. isInch() {
  850. [[ "$1" = 'in' || "$1" = 'inch' || "$1" = 'inches' ]] && return $TRUE
  851. return $FALSE
  852. }
  853. # Returns $TRUE if $1 is a valid point string, $FALSE otherwise
  854. isPoint() {
  855. [[ "$1" = 'pt' || "$1" = 'pts' || "$1" = 'point' || "$1" = 'points' ]] && return $TRUE
  856. return $FALSE
  857. }
  858. # Returns $TRUE if a custom paper is being used, $FALSE otherwise
  859. isCustomPaper() {
  860. return $CUSTOM_RESIZE_PAPER
  861. }
  862. # Returns $FALSE if a custom paper is being used, $TRUE otherwise
  863. isNotCustomPaper() {
  864. isCustomPaper && return $FALSE
  865. return $TRUE
  866. }
  867. ######################### CONVERSIONS ##########################
  868. # Prints the lowercase char value for $1
  869. lowercaseChar() {
  870. case "$1" in
  871. [A-Z])
  872. n=$(printf "%d" "'$1")
  873. n=$((n+32))
  874. printf \\$(printf "%o" "$n")
  875. ;;
  876. *)
  877. printf "%s" "$1"
  878. ;;
  879. esac
  880. }
  881. # Prints the lowercase version of a string
  882. lowercase() {
  883. word="$@"
  884. for((i=0;i<${#word};i++))
  885. do
  886. ch="${word:$i:1}"
  887. lowercaseChar "$ch"
  888. done
  889. }
  890. # Prints the uppercase char value for $1
  891. uppercaseChar(){
  892. case "$1" in
  893. [a-z])
  894. n=$(printf "%d" "'$1")
  895. n=$((n-32))
  896. printf \\$(printf "%o" "$n")
  897. ;;
  898. *)
  899. printf "%s" "$1"
  900. ;;
  901. esac
  902. }
  903. # Prints the uppercase version of a string
  904. uppercase() {
  905. word="$@"
  906. for((i=0;i<${#word};i++))
  907. do
  908. ch="${word:$i:1}"
  909. uppercaseChar "$ch"
  910. done
  911. }
  912. # Prints the postscript points rounded equivalent from $1 mm
  913. milimetersToPoints() {
  914. local pts=$(echo "scale=8; $1 * 72 / 25.4" | "$BCBIN")
  915. printf '%.0f' "$pts" # Print rounded conversion
  916. }
  917. # Prints the postscript points rounded equivalent from $1 inches
  918. inchesToPoints() {
  919. local pts=$(echo "scale=8; $1 * 72" | "$BCBIN")
  920. printf '%.0f' "$pts" # Print rounded conversion
  921. }
  922. # Prints the mm equivalent from $1 postscript points
  923. pointsToMilimeters() {
  924. local pts=$(echo "scale=8; $1 / 72 * 25.4" | "$BCBIN")
  925. printf '%.0f' "$pts" # Print rounded conversion
  926. }
  927. # Prints the inches equivalent from $1 postscript points
  928. pointsToInches() {
  929. local pts=$(echo "scale=8; $1 / 72" | "$BCBIN")
  930. printf '%.1f' "$pts" # Print rounded conversion
  931. }
  932. ######################## MODE-DETECTION ########################
  933. # Returns $TRUE if the scale was set manually, $FALSE if we are using automatic scaling
  934. isManualScaledMode() {
  935. [[ $AUTOMATIC_SCALING -eq $TRUE ]] && return $FALSE
  936. return $TRUE
  937. }
  938. # Returns true if we are resizing a paper (ignores scaling), false otherwise
  939. isResizeMode() {
  940. isEmpty $RESIZE_PAPER_TYPE && return $FALSE
  941. return $TRUE
  942. }
  943. # Returns true if we are resizing a paper and the scale was manually set
  944. isMixedMode() {
  945. isResizeMode && isManualScaledMode && return $TRUE
  946. return $FALSE
  947. }
  948. # Return $TRUE if adaptive mode is enabled, $FALSE otherwise
  949. isAdaptiveMode() {
  950. return $ADAPTIVEMODE
  951. }
  952. # Return $TRUE if adaptive mode is disabled, $FALSE otherwise
  953. isNotAdaptiveMode() {
  954. isAdaptiveMode && return $FALSE
  955. return $TRUE
  956. }
  957. ########################## VALIDATORS ##########################
  958. # Returns $TRUE if $PGWIDTH OR $PGWIDTH are empty or NOT an Integer, $FALSE otherwise
  959. pageSizeIsInvalid() {
  960. if isNotAnInteger "$PGWIDTH" || isNotAnInteger "$PGHEIGHT"; then
  961. return $TRUE
  962. fi
  963. return $FALSE
  964. }
  965. # Return $TRUE if $1 is empty, $FALSE otherwise
  966. isEmpty() {
  967. [[ -z "$1" ]] && return $TRUE
  968. return $FALSE
  969. }
  970. # Return $TRUE if $1 is NOT empty, $FALSE otherwise
  971. isNotEmpty() {
  972. [[ -z "$1" ]] && return $FALSE
  973. return $TRUE
  974. }
  975. # Returns $TRUE if $1 is an integer, $FALSE otherwise
  976. isAnInteger() {
  977. case $1 in
  978. ''|*[!0-9]*) return $FALSE ;;
  979. *) return $TRUE ;;
  980. esac
  981. }
  982. # Returns $TRUE if $1 is NOT an integer, $FALSE otherwise
  983. isNotAnInteger() {
  984. case $1 in
  985. ''|*[!0-9]*) return $TRUE ;;
  986. *) return $FALSE ;;
  987. esac
  988. }
  989. # Returns $TRUE if $1 is a floating point number (or an integer), $FALSE otherwise
  990. isFloat() {
  991. [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ ]] && return $TRUE
  992. return $FALSE
  993. }
  994. # Returns $TRUE if $1 is a floating point number bigger than zero, $FALSE otherwise
  995. isFloatBiggerThanZero() {
  996. isFloat "$1" && [[ (( $1 > 0 )) ]] && return $TRUE
  997. return $FALSE
  998. }
  999. # Returns $TRUE if $1 is readable, $FALSE otherwise
  1000. isReadable() {
  1001. [[ -r "$1" ]] && return $TRUE
  1002. return $FALSE;
  1003. }
  1004. # Returns $TRUE if $1 is a directory, $FALSE otherwise
  1005. isDir() {
  1006. [[ -d "$1" ]] && return $TRUE
  1007. return $FALSE;
  1008. }
  1009. # Returns 0 if succeded, other integer otherwise
  1010. isTouchable() {
  1011. touch "$1" 2>/dev/null
  1012. }
  1013. # Returns $TRUE if $1 has a .pdf extension, false otherwsie
  1014. isPDF() {
  1015. [[ "$(lowercase $1)" =~ ^..*\.pdf$ ]] && return $TRUE
  1016. return $FALSE
  1017. }
  1018. # Returns $TRUE if $1 is a file, false otherwsie
  1019. isFile() {
  1020. [[ -f "$1" ]] && return $TRUE
  1021. return $FALSE
  1022. }
  1023. # Returns $TRUE if $1 is NOT a file, false otherwsie
  1024. isNotFile() {
  1025. [[ -f "$1" ]] && return $FALSE
  1026. return $TRUE
  1027. }
  1028. # Returns $TRUE if $1 is executable, false otherwsie
  1029. isExecutable() {
  1030. [[ -x "$1" ]] && return $TRUE
  1031. return $FALSE
  1032. }
  1033. # Returns $TRUE if $1 is NOT executable, false otherwsie
  1034. isNotExecutable() {
  1035. [[ -x "$1" ]] && return $FALSE
  1036. return $TRUE
  1037. }
  1038. # Returns $TRUE if $1 is a file and executable, false otherwsie
  1039. isAvailable() {
  1040. if isFile "$1" && isExecutable "$1"; then
  1041. return $TRUE
  1042. fi
  1043. return $FALSE
  1044. }
  1045. # Returns $TRUE if $1 is NOT a file or NOT executable, false otherwsie
  1046. isNotAvailable() {
  1047. if isNotFile "$1" || isNotExecutable "$1"; then
  1048. return $TRUE
  1049. fi
  1050. return $FALSE
  1051. }
  1052. ###################### PRINTING TO SCREEN ######################
  1053. # Prints version
  1054. printVersion() {
  1055. local vStr=""
  1056. [[ "$2" = 'verbose' ]] && vStr=" - Verbose Execution"
  1057. local strBanner="$PDFSCALE_NAME v$VERSION$vStr"
  1058. if [[ $1 -eq 2 ]]; then
  1059. printError "$strBanner"
  1060. elif [[ $1 -eq 3 ]]; then
  1061. local extra="$(isNotEmpty "$2" && echo "$2")"
  1062. echo "$strBanner$extra"
  1063. else
  1064. vprint "$strBanner"
  1065. fi
  1066. }
  1067. # Prints input, output file info, if verbosing
  1068. vPrintFileInfo() {
  1069. vprint " Input File: $INFILEPDF"
  1070. vprint " Output File: $OUTFILEPDF"
  1071. }
  1072. # Prints the scale factor to screen, or custom message
  1073. vPrintScaleFactor() {
  1074. local scaleMsg="$SCALE"
  1075. isNotEmpty "$1" && scaleMsg="$1"
  1076. vprint " Scale Factor: $scaleMsg"
  1077. }
  1078. # Prints help info
  1079. printHelp() {
  1080. printVersion 3
  1081. local paperList="$(printPaperNames)"
  1082. echo "
  1083. Usage: $PDFSCALE_NAME <inFile.pdf>
  1084. $PDFSCALE_NAME -i <inFile.pdf>
  1085. $PDFSCALE_NAME [-v] [-s <factor>] [-m <page-detection>] <inFile.pdf> [outfile.pdf]
  1086. $PDFSCALE_NAME [-v] [-r <paper>] [-f <flip-detection>] [-a <auto-rotation>] <inFile.pdf> [outfile.pdf]
  1087. $PDFSCALE_NAME -p
  1088. $PDFSCALE_NAME -h
  1089. $PDFSCALE_NAME -V
  1090. Parameters:
  1091. -v, --verbose
  1092. Verbose mode, prints extra information
  1093. Use twice for timestamp
  1094. -h, --help
  1095. Print this help to screen and exits
  1096. -V, --version
  1097. Prints version to screen and exits
  1098. -n, --no-overwrite
  1099. Aborts execution if the output PDF file already exists
  1100. By default, the output file will be overwritten
  1101. -m, --mode <mode>
  1102. Paper size detection mode
  1103. Modes: a, adaptive Default mode, tries all the methods below
  1104. g, grep Forces the use of Grep method
  1105. m, mdls Forces the use of MacOS Quartz mdls
  1106. p, pdfinfo Forces the use of PDFInfo
  1107. i, identify Forces the use of ImageMagick's Identify
  1108. -i, --info <file>
  1109. Prints <file> Paper Size information to screen and exits
  1110. -s, --scale <factor>
  1111. Changes the scaling factor or forces mixed mode
  1112. Defaults: $SCALE (scale mode) / Disabled (resize mode)
  1113. MUST be a number bigger than zero
  1114. Eg. -s 0.8 for 80% of the original size
  1115. -r, --resize <paper>
  1116. Triggers the Resize Paper Mode, disables auto-scaling of $SCALE
  1117. Resize PDF and fit-to-page
  1118. <paper> can be: source, custom or a valid std paper name, read below
  1119. -f, --flip-detect <mode>
  1120. Flip Detection Mode, defaults to 'auto'
  1121. Inverts Width <-> Height of a Resized PDF
  1122. Modes: a, auto Keeps source orientation, default
  1123. f, force Forces flip W <-> H
  1124. d, disable Disables flipping
  1125. -a, --auto-rotate <mode>
  1126. Setting for GS -dAutoRotatePages, defaults to 'PageByPage'
  1127. Uses text-orientation detection to set Portrait/Landscape
  1128. Modes: p, pagebypage Auto-rotates pages individually
  1129. n, none Retains orientation of each page
  1130. a, all Rotates all pages (or none) depending
  1131. on a kind of \"majority decision\"
  1132. -p, --print-papers
  1133. Prints Standard Paper info tables to screen and exits
  1134. Scaling Mode:
  1135. - The default mode of operation is scaling mode with fixed paper
  1136. size and scaling pre-set to $SCALE
  1137. - By not using the resize mode you are using scaling mode
  1138. - Flip-Detection and Auto-Rotation are disabled in Scaling mode,
  1139. you can use '-r source -s <scale>' to override.
  1140. Resize Paper Mode:
  1141. - Disables the default scaling factor! ($SCALE)
  1142. - Changes the PDF Paper Size in points. Will fit-to-page
  1143. Mixed Mode:
  1144. - In mixed mode both the -s option and -r option must be specified
  1145. - The PDF will be first resized then scaled
  1146. Output filename:
  1147. - Having the extension .pdf on the output file name is optional,
  1148. it will be added if not present.
  1149. - The output filename is optional. If no file name is passed
  1150. the output file will have the same name/destination of the
  1151. input file with added suffixes:
  1152. .SCALED.pdf is added to scaled files
  1153. .<PAPERSIZE>.pdf is added to resized files
  1154. .<PAPERSIZE>.SCALED.pdf is added in mixed mode
  1155. Standard Paper Names: (case-insensitive)
  1156. $paperList
  1157. Custom Paper Size:
  1158. - Paper size can be set manually in Milimeters, Inches or Points
  1159. - Custom paper definition MUST be quoted into a single parameter
  1160. - Actual size is applied in points (mms and inches are transformed)
  1161. - Measurements: mm, mms, milimeters
  1162. pt, pts, points
  1163. in, inch, inches
  1164. Use: $PDFSCALE_NAME -r 'custom <measurement> <width> <height>'
  1165. Ex: $PDFSCALE_NAME -r 'custom mm 300 300'
  1166. Using Source Paper Size: (no-resizing)
  1167. - Wildcard 'source' is used used to keep paper size the same as the input
  1168. - Usefull to run Auto-Rotation without resizing
  1169. - Eg. $PDFSCALE_NAME -r source ./input.dpf
  1170. Options and Parameters Parsing:
  1171. - From v2.1.0 (long-opts) there is no need to pass file names at the end
  1172. - Anything that is not a short-option is case-insensitive
  1173. - Short-options: case-sensitive Eg. -v for Verbose, -V for Version
  1174. - Long-options: case-insensitive Eg. --SCALE and --scale are the same
  1175. - Subparameters: case-insensitive Eg. -m PdFinFo is valid
  1176. - Grouping short-options is not supported Eg. -vv, or -vs 0.9
  1177. Additional Notes:
  1178. - File and folder names with spaces should be quoted or escaped
  1179. - The scaling is centered and using a scale bigger than 1.0 may
  1180. result on cropping parts of the PDF
  1181. - For detailed paper types information, use: $PDFSCALE_NAME -p
  1182. Examples:
  1183. $PDFSCALE_NAME myPdfFile.pdf
  1184. $PDFSCALE_NAME -i '/home/My Folder/My PDF File.pdf'
  1185. $PDFSCALE_NAME myPdfFile.pdf \"My Scaled Pdf\"
  1186. $PDFSCALE_NAME -v -v myPdfFile.pdf
  1187. $PDFSCALE_NAME -s 0.85 myPdfFile.pdf My\\ Scaled\\ Pdf.pdf
  1188. $PDFSCALE_NAME -m pdfinfo -s 0.80 -v myPdfFile.pdf
  1189. $PDFSCALE_NAME -v -v -m i -s 0.7 myPdfFile.pdf
  1190. $PDFSCALE_NAME -r A4 myPdfFile.pdf
  1191. $PDFSCALE_NAME -v -v -r \"custom mm 252 356\" -s 0.9 -f \"../input file.pdf\" \"../my new pdf\"
  1192. "
  1193. }
  1194. # Prints usage info
  1195. usage() {
  1196. [[ "$2" != 'nobanner' ]] && printVersion 2
  1197. isNotEmpty "$1" && printError $'\n'"$1"
  1198. printError $'\n'"Usage: $PDFSCALE_NAME [-v] [-s <factor>] [-m <mode>] [-r <paper> [-f <mode>] [-a <mode>]] <inFile.pdf> [outfile.pdf]"
  1199. printError "Help : $PDFSCALE_NAME -h"
  1200. }
  1201. # Prints Verbose information
  1202. vprint() {
  1203. [[ $VERBOSE -eq 0 ]] && return $TRUE
  1204. timestamp=""
  1205. [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | "
  1206. echo "$timestamp$1"
  1207. }
  1208. # Prints dependency information and aborts execution
  1209. printDependency() {
  1210. printVersion 2
  1211. local brewName="$1"
  1212. [[ "$1" = 'pdfinfo' && "$OSNAME" = "Darwin" ]] && brewName="xpdf"
  1213. printError $'\n'"ERROR! You need to install the package '$1'"$'\n'
  1214. printError "Linux apt-get.: sudo apt-get install $1"
  1215. printError "Linux yum.....: sudo yum install $1"
  1216. printError "MacOS homebrew: brew install $brewName"
  1217. printError $'\n'"Aborting..."
  1218. exit $EXIT_MISSING_DEPENDENCY
  1219. }
  1220. # Prints initialization errors and aborts execution
  1221. initError() {
  1222. local errStr="$1"
  1223. local exitStat=$2
  1224. isEmpty "$exitStat" && exitStat=$EXIT_ERROR
  1225. usage "ERROR! $errStr" "$3"
  1226. exit $exitStat
  1227. }
  1228. # Prints to stderr
  1229. printError() {
  1230. echo >&2 "$@"
  1231. }
  1232. ########################## EXECUTION ###########################
  1233. initDeps
  1234. getOptions "${@}"
  1235. main
  1236. exit $?